写在最前面:这儿所指的自界说GIF控件指的是一个承继自imageview的、运用帧动画将一系列图片播映出来的控件,并不是读取GIF图片播映的控件
一、完结内容
-
创立一个自界说控件,能够播映图片资源达成相似GIF的作用
-
在自界说控件中运用自界说特点
-
运用MVVM结构,在viewmodel中能够设置图片资源
二、学习目标
-
学习怎么设置自界说特点
-
学习怎么将自界说特点进行数据绑定
三、完结
1. 自界说控件
首要完结根本的内容,自界说GIF控件,首要运用到的便是帧动画
public class GifView extends AppCompatImageView {
private AnimationDrawable animationDrawable;
public GifView(Context context) {
super(context);
init();
}
public GifView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
animationDrawable = new AnimationDrawable();
setImageDrawable(animationDrawable);
int[] gifResourceIds = {R.drawable.frame_1, R.drawable.frame_2, R.drawable.frame_3};
setGifResourceIds(gifResourceIds,100);
startGif();
}
public void setGifResourceIds(int[] resourceIds, int duration) {
for (int resourceId : resourceIds) {
animationDrawable.addFrame(getResources().getDrawable(resourceId), duration);
}
}
public void startGif() {
animationDrawable.start();
}
public void stopGif() {
animationDrawable.stop();
}
}
在xml文件中这样就能够运用这个自界说控件
<com.example.gif.GifView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
这是我最开端写的版本,只是完结了播映动画的要求,图片也是写死在里面的,当然我也能够挑选在MainActivity运用findViewByID,再调用GifView中的setGifResourceIds去设置图片,可是假如我想用MVVM结构的话,这便是一个不合适的写法,所以我考虑运用自界说特点。
2. 自界说特点
- 在res/values/attrs.xml中界说自界说特点:
创立一个XML文件,通常命名为attrs.xml
,位于res/values/
目录下。在这个文件中,你能够界说自己的特点。例如:
<resources>
<declare-styleable name="GifView">
<attr name="gifResourceIds" format="reference" />
<attr name="gifDuration" format="integer" />
</declare-styleable>
</resources>
这儿我界说了两个特点,可是后面实际运用我只用了一个,假如咱们有爱好能够自己完结第二个自界说特点的设置。这儿gifResourceIds我运用的format是reference,指向一个引证id
- 在布局文件中运用自界说特点: 在布局文件中,运用自界说特点给相应的控件设置值。
首要添加一个新的XML命名空间声明,它告知Android体系怎么解释和处理XML布局文件中的自界说特点。
xmlns:app="http://schemas.android.com/apk/res-auto"
然后就能够运用这个特点
<com.example.gif.GifView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:gifResourceIds=""
/>
可是因为咱们还并没有在自界说view中读取和运用这个特点,所以现在它是没有意义的。不过这儿我遇到了一个新的问题,我这儿想传入的特点值应该是一堆图片——便是我想让他们像GIF一样播映出来的一堆图片,所以我应该传入一个相似数组的东西。
在这儿我的解决方案是,在xml中装备一个数组资源,其间包括我想要播映的图片。我之前有说到自界说特点的format是reference,所以我这儿传入一个资源id就能够了。
装备数组资源:
在res/values/
目录下创立一个XML文件(例如,arrays.xml):
<resources>
<array name="gifResourceIds">
<item>@drawable/frame_1</item>
<item>@drawable/frame_2</item>
<item>@drawable/frame_3</item>
</array>
</resources>
这样咱们就可通过它的资源id R.array.gifResourceIds
来获取它了
- 在自界说控件中读取和运用自界说特点:
现在回到之前的内容,在自界说控件的Java类中,咱们需求读取和运用自界说特点的值,这样这个特点才有意义。在结构函数或初始化方法中,能够运用obtainStyledAttributes
方法获取特点值。
TypedArray ta=context.obtainStyledAttributes(attrs,R.styleable.GifView);
resData=ta.getResourceId(R.styleable.GifView_gifResourceIds,0);
这儿获取该特点的资源ID,并将其存储在变量 resData
中
- 解析数据
这儿咱们获取了这个数据,可是它是这个数组的资源id,咱们需求的是数组内的图片资源,所以咱们需求将其间的数据读取出来
private void initRes() {
// 获取引证的资源 ID 数组
if (resData == Resources.ID_NULL) {
return;
}
TypedArray resourceArray = getResources().obtainTypedArray(resData);
int length = resourceArray.length();
resourceIds.clear();
for (int i = 0; i < length; i++) {
resourceIds.add(resourceArray.getResourceId(i, 0));
}
resourceArray.recycle();
}
通过这儿咱们终于取得图片资源
3. GifView代码
这儿先贴上悉数源码
public class GifView extends AppCompatImageView {
private final String TAG= "GifView";
private AnimationDrawable animationDrawable;
private int resData;
private ArrayList<Integer> resourceIds = new ArrayList<>();
private ArrayList<Integer> temp;
public GifView(Context context) {
super(context);
Log.d(TAG, "GifView: no param");
}
public GifView(Context context, AttributeSet attrs) {
super(context, attrs);
Log.d(TAG, "GifView: have param");
TypedArray ta=context.obtainStyledAttributes(attrs,R.styleable.GifView);
Log.d(TAG, "GifView: ta"+ta);
Log.d(TAG, "GifView: ta.getIndexCount()"+ta.getIndexCount());
resData=ta.getResourceId(R.styleable.GifView_gifResourceIds,0);
initRes();
init();
}
private void initRes() {
// 获取引证的资源 ID 数组
if (resData == Resources.ID_NULL) {
Log.d(TAG, "initRes: resource id is null");
return;
}
TypedArray resourceArray = getResources().obtainTypedArray(resData);
int length = resourceArray.length();
resourceIds.clear();
for (int i = 0; i < length; i++) {
resourceIds.add(resourceArray.getResourceId(i, 0));
}
resourceArray.recycle();
}
public void setGifResourceIds(int gifResourceIds) {
resData= gifResourceIds;
initRes();
init();
}
private void init() {
if (animationDrawable != null) {
if (animationDrawable.isRunning()) {
animationDrawable.stop();
}
}
animationDrawable = new AnimationDrawable();
setImageDrawable(animationDrawable);
if (resourceIds != null && resourceIds.size() > 0) {
for (int resourceId : resourceIds) {
animationDrawable.addFrame(getResources().getDrawable(resourceId), 100);
}
}
startGif();
}
public void startGif() {
animationDrawable.start();
}
public void stopGif() {
animationDrawable.stop();
}
}
现在咱们的自界说特点已经能够运用了,咱们能够尝试一下
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.example.gifviewmvvm.GifView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:gifResourceIds="@array/gifResourceIds"
/>
</LinearLayout>
假如图片能够动了就成功了
4. Viewmodel
现在来写viewmodel,在咱们这个viewmodel只需求完结一个简单的设置资源的功能
public class GifViewModel extends ViewModel {
private MutableLiveData<Integer> gifResourceIds = new MutableLiveData<>();
public GifViewModel(){
setGifResourceIds();
}
public void setGifResourceIds(){
gifResourceIds.setValue(R.array.gifResourceIds);
}
public MutableLiveData<Integer> getGifResourceIds() {
return gifResourceIds;
}
}
5. MainActvity
binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
viewModel = new ViewModelProvider(this).get(GifViewModel.class);
binding.setViewModel(viewModel);
binding.setLifecycleOwner(this);
现在一切内容都完结了!能够试试在xml运用双向数据绑定了
<com.example.gifviewmvvm.GifView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:gifResourceIds="@{viewModel.gifResourceIds}"
/>
能够尝试再在页面做一个按钮,完结点击按钮替换图片资源的作用,体会mvvm结构的便当。
四、总结
通过这次练习感觉对于MVVM结构愈加清晰了一些,不过这个自界说控件是承继自imageview的,所以写起来并不需求measure,layout,draw的流程,降低了难度,今后能够试试更复杂的版本。