Android中标签容器控件的实例详解

Android中标签容器控件的实例详解,第1张

概述前言在一些APP中我们可以看到一些存放标签容器控件,和我们平时使用的一些布局方式有些不同,它们一般都可以自动适应屏幕的宽度进行布局,根据对自定义控件的一些理解,今天写一个简单的标签容器控件,给大家参考

前言

在一些APP中我们可以看到一些存放标签的容器控件,和我们平时使用的一些布局方式有些不同,它们一般都可以自动适应屏幕的宽度进行布局,根据对自定义控件的一些理解,今天写一个简单的标签容器控件,给大家参考学习。

下面这个是我在手机上截取的一个实例,是在Miui8系统上截取的

这个是我实现的效果图

原理介绍

根据对整个控件的效果分析,大致可以将控件分别从以下这几个角度进行分析:

1.首先涉及到自定义的VIEwGroup,因为现有的控件没法满足我们的布局效果,就涉及到要重写onMeasure和onLayout,这里需要注意的问题是自定义view的时候,我们需要考虑到VIEw的padding属性,而在自定义viewGroup中我们需要在onLayout中考虑Child控件的margin属性否则子类设置这个属性将会失效。整个VIEw的绘制流程是这样的:

最顶层的VIEwRoot执行performTraversals然后分别开始对各个VIEw进行层级的测量、布局、绘制,整个流程是一层一层进行的,也就是说父视图测量时会调用子视图的测量方法,子视图调孙视图方法,一直测量到叶子节点,performTraversals这个函数翻译过来很直白,执行遍历,就说明了这种层级关系。

2.该控件形式上和ListVIEw的形式比较相近,所以在这里我也模仿ListVIEw的Adapter模式实现了对控件内容的 *** 作,这里对ListVIEw的setAdapter和Adapter的notifyDataSetChanged方法做个简单的解释:

在ListVIEw调用setAdapter后,ListVIEw会去注册一个Observer对象到这个adapter上,然后当我们在改变设置到adapter上的数据发改变时,我们会调用adapter的notifyDataSetChanged方法,这个方法就会通知所有监听了该Adapter数据改变时的Observer对象,这就是典型的监听者模式,这时由于ListVIEw中的内部成员对象监听了该事件,就可以知道数据源发生了改变,我们需要对真个控件重新进行绘制了,下面来一些相关的源码。

Adapter的notifyDataSetChanged

public voID notifyDataSetChanged() {    mDataSetobservable.notifyChanged();  }

ListVIEw的setAdapter方法

@OverrIDe  public voID setAdapter(listadapter adapter) {    /**     *每次设置新的适配的时候,如果现在有的话会做一个解除监听的 *** 作     */    if (mAdapter != null && mDataSetobserver != null) {      mAdapter.unregisterDataSetobserver(mDataSetobserver);    }    resetList();    mRecycler.clear();    /** 省略部分代码.....  */    if (mAdapter != null) {      mAreAllitemsSelectable = mAdapter.areAllitemsEnabled();      moldItemCount = mItemCount;      mItemCount = mAdapter.getCount();      checkFocus();      /**      *在这里对adapter设置了监听,      *使用的是AdapterDataSetobserver类的对象,该对象定义在ListVIEw的父类AdapterVIEw中      */      mDataSetobserver = new AdapterDataSetobserver();      mAdapter.registerDataSetobserver(mDataSetobserver);      /** 省略 */    } else {      /** 省略 */    }    requestLayout();  }

AdapterVIEw中的内部类AdapterDataSetobserver

class AdapterDataSetobserver extends DataSetobserver {    private Parcelable mInstanceState = null;    @OverrIDe    public voID onChanged() {      /* ***代码略*** */      checkFocus();      requestLayout();    }    @OverrIDe    public voID onInvalIDated() {      /* ***代码略*** */      checkFocus();      requestLayout();    }    public voID clearSavedState() {      mInstanceState = null;    }  }

一段伪代码表示

ListVIEw{  Observer observer{     onChange(){       change;     }  }  setAdapter(Adapter adapter){     adapter.register(observer);  }}Adapter{  List<Observer> mObservable;  register(observer){    mObservable.add(observer);  }  notifyDataSetChanged(){    for(i-->mObserverable.size()){      mObserverable.get(i).onChange    }  }}

实现过程

获取VIEwItem的接口

package humoursz.grIDtag.test.adapter;import androID.vIEw.VIEw;import java.util.List;/** * Created by zhangzhiquan on 2016/7/19. */public interface GrIDeTagBaseAdapter {  List<VIEw> getVIEws();}

抽象适配器AbsGrIDTagsAdapter

package humoursz.grIDtag.test.adapter;import androID.database.DataSetobservable;import androID.database.DataSetobserver;/** * Created by zhangzhiquan on 2016/7/19. */public abstract class AbsGrIDTagsAdapter implements GrIDeTagBaseAdapter {  DataSetobservable mObservable = new DataSetobservable();  public voID notification(){    mObservable.notifyChanged();  }  public voID registerObserve(DataSetobserver observer){    mObservable.registerObserver(observer);  }  public voID unregisterObserve(DataSetobserver observer){    mObservable.unregisterObserver(observer);  }}

此效果中的需要的适配器,实现了getVIEw接口,主要是模仿了ListVIEw的BaseAdapter

package humoursz.grIDtag.test.adapter;import androID.content.Context;import androID.vIEw.LayoutInflater;import androID.vIEw.VIEw;import androID.Widget.TextVIEw;import java.util.ArrayList;import java.util.List;import humoursz.grIDtag.test.R;import humoursz.grIDtag.test.util.UIUtil;import humoursz.grIDtag.test.Widget.GrIDTagVIEw;/** * Created by zhangzhiquan on 2016/7/19. */public class MyGrIDTagAdapter extends AbsGrIDTagsAdapter {  private Context mContext;  private List<String> mTags;  public MyGrIDTagAdapter(Context context,List<String> Tags) {    mContext = context;    mTags = Tags;  }  @OverrIDe  public List<VIEw> getVIEws() {    List<VIEw> List = new ArrayList<>();    for (int i = 0; i < mTags.size(); i++) {      TextVIEw tv = (TextVIEw) LayoutInflater.from(mContext)          .inflate(R.layout.grID_tag_item_text,null);      tv.setText(mTags.get(i));      GrIDTagVIEw.LayoutParams lp = new GrIDTagVIEw          .LayoutParams(GrIDTagVIEw.LayoutParams.WRAP_CONTENT,GrIDTagVIEw.LayoutParams.WRAP_CONTENT);      lp.margin(UIUtil.dp2px(mContext,5));      tv.setLayoutParams(lp);      List.add(tv);    }    return List;  }}

最后是主角GrIDTagsVIEw控件

package humoursz.grIDtag.test.Widget;import androID.content.Context;import androID.database.DataSetobserver;import androID.util.AttributeSet;import androID.util.Log;import androID.vIEw.VIEw;import androID.vIEw.VIEwGroup;import java.util.List;import humoursz.grIDtag.test.adapter.AbsGrIDTagsAdapter;/** * Created by zhangzhiquan on 2016/7/18. */public class GrIDTagVIEw extends VIEwGroup {  private int mlines = 1;  private int mWIDthSize = 0;  private AbsGrIDTagsAdapter mAdapter;  private GTObserver mObserver = new GTObserver();  public GrIDTagVIEw(Context context) {    this(context,null);  }  public GrIDTagVIEw(Context context,AttributeSet attrs) {    this(context,attrs,0);  }  public GrIDTagVIEw(Context context,AttributeSet attrs,int defStyleAttr) {    super(context,defStyleAttr);  }  public voID setAdapter(AbsGrIDTagsAdapter adapter) {    if (mAdapter != null) {      mAdapter.unregisterObserve(mObserver);    }    mAdapter = adapter;    mAdapter.registerObserve(mObserver);    mAdapter.notification();  }  @OverrIDe  protected voID onMeasure(int wIDthMeasureSpec,int heightmeasureSpec) {    int wIDthSize = MeasureSpec.getSize(wIDthMeasureSpec);    int heightSize = MeasureSpec.getSize(heightmeasureSpec);    int curWIDthSize = 0;    int childHeight = 0;    mlines = 1;    for (int i = 0; i < getChildCount(); ++i) {      VIEw child = getChildAt(i);      measureChild(child,wIDthMeasureSpec,heightmeasureSpec);      curWIDthSize += getChildRealWIDthSize(child);      if (curWIDthSize > wIDthSize) {        /**         * 计算一共需要多少行,用于计算控件的高度         * 计算方法是,如果当前控件放下后宽度超过         * 容器本身的高度,就放到下一行         */        curWIDthSize = getChildRealWIDthSize(child);        mlines++;      }      if (childHeight == 0) {        /**         * 在第一次计算时拿到字视图的高度作为计算基础         */        childHeight = getChildRealHeightSize(child);      }    }    mWIDthSize = wIDthSize;    setMeasuredDimension(wIDthSize,childHeight == 0 ? heightSize : childHeight * mlines);  }  @OverrIDe  protected voID onLayout(boolean changed,int l,int t,int r,int b) {    if (getChildCount() == 0)      return;    int childCount = getChildCount();    LayoutParams lp = getChildLayoutParams(getChildAt(0));    /**     * 初始的左边界在自身的padding left和child的margin后     * 初始的上边界原理相同     */    int left = getpaddingleft() + lp.leftmargin;    int top = getpaddingtop() + lp.topmargin;    int curleft = left;    for (int i = 0; i < childCount; ++i) {      VIEw child = getChildAt(i);      int right = curleft + getChildRealWIDthSize(child);      /**       * 计算如果放下当前试图后整个一行到右侧的距离       * 如果超过控件宽那就放到下一行,并且左边距还原,上边距等于下一行的开始       */      if (right > mWIDthSize) {        top += getChildRealHeightSize(child);        curleft = left;      }      child.layout(curleft,top,curleft + child.getMeasureDWIDth(),top + child.getMeasuredHeight());      /**       * 下一个控件的左边开始距离是上一个控件的右边       */      curleft += getChildRealWIDthSize(child);    }  }  /**   * 获取childVIEw实际占用宽度   * @param child   * @return 控件实际占用的宽度,需要算上margin否则margin不生效   */  private int getChildRealWIDthSize(VIEw child) {    LayoutParams lp = getChildLayoutParams(child);    int size = child.getMeasureDWIDth() + lp.leftmargin + lp.rightmargin;    return size;  }  /**   * 获取childVIEw实际占用高度   * @param child   * @return 实际占用高度需要考虑上下margin   */  private int getChildRealHeightSize(VIEw child) {    LayoutParams lp = getChildLayoutParams(child);    int size = child.getMeasuredHeight() + lp.topmargin + lp.bottommargin;    return size;  }  /**   * 获取LayoutParams属性   * @param child   * @return   */  private LayoutParams getChildLayoutParams(VIEw child) {    LayoutParams lp;    if (child.getLayoutParams() instanceof LayoutParams) {      lp = (LayoutParams) child.getLayoutParams();    } else {      lp = (LayoutParams) generateLayoutParams(child.getLayoutParams());    }    return lp;  }  @OverrIDe  public VIEwGroup.LayoutParams generateLayoutParams(AttributeSet attr) {    return new LayoutParams(getContext(),attr);  }  @OverrIDe  protected VIEwGroup.LayoutParams generateLayoutParams(VIEwGroup.LayoutParams p) {    return new LayoutParams(p);  }  public static class LayoutParams extends marginLayoutParams {    public LayoutParams(Context c,AttributeSet attrs) {      super(c,attrs);    }    public LayoutParams(int wIDth,int height) {      super(wIDth,height);    }    public LayoutParams(marginLayoutParams source) {      super(source);    }    public LayoutParams(VIEwGroup.LayoutParams source) {      super(source);    }    public voID marginleft(int left) {      this.leftmargin = left;    }    public voID marginRight(int r) {      this.rightmargin = r;    }    public voID margintop(int t) {      this.topmargin = t;    }    public voID marginBottom(int b) {      this.bottommargin = b;    }    public voID margin(int m){      this.leftmargin = m;      this.rightmargin = m;      this.topmargin = m;      this.bottommargin = m;    }  }  private class GTObserver extends DataSetobserver {    @OverrIDe    public voID onChanged() {      removeAllVIEws();      List<VIEw> List = mAdapter.getVIEws();      for (int i = 0; i < List.size(); i++) {        addVIEw(List.get(i));      }    }    @OverrIDe    public voID onInvalIDated() {      Log.d("Mrz","fd");    }  }}

MainActivity

package humoursz.grIDtag.test;import androID.support.v7.app.AppCompatActivity;import androID.os.Bundle;import androID.vIEw.VIEw;import java.util.List;import humoursz.grIDtag.test.adapter.MyGrIDTagAdapter;import humoursz.grIDtag.test.util.ListUtil;import humoursz.grIDtag.test.Widget.GrIDTagVIEw;public class MainActivity extends AppCompatActivity {  MyGrIDTagAdapter adapter;  GrIDTagVIEw mGrIDTag;  List<String> mList;  @OverrIDe  protected voID onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentVIEw(R.layout.activity_main);    mGrIDTag = (GrIDTagVIEw)findVIEwByID(R.ID.grID_Tags);    mList = ListUtil.getGrIDTagsList(20);    adapter = new MyGrIDTagAdapter(this,mList);    mGrIDTag.setAdapter(adapter);  }  public voID onClick(VIEw v){    mList.removeAll(mList);    mList.addAll(ListUtil.getGrIDTagsList(20));    adapter.notification();  }}

XML 文件

<?xml version="1.0" enCoding="utf-8"?><relativeLayout xmlns:androID="http://schemas.androID.com/apk/res/androID"  xmlns:tools="http://schemas.androID.com/tools"  androID:layout_wIDth="match_parent"  androID:layout_height="match_parent"  tools:context="humoursz.grIDtag.test.MainActivity">  <humoursz.grIDtag.test.Widget.GrIDTagVIEw    androID:ID="@+ID/grID_Tags"    androID:layout_wIDth="match_parent"    androID:layout_height="wrap_content">  </humoursz.grIDtag.test.Widget.GrIDTagVIEw>  <button    androID:layout_centerInParent="true"    androID:layout_wIDth="wrap_content"    androID:layout_height="wrap_content"    androID:onClick="onClick"    androID:text="换一批"/></relativeLayout>

以上就是AndroID中标签容器控件的全部实现过程,这样一个简单的控件就写好了,主要需要注意measure和layout否则很多效果都会失效,安卓中的linearLayout之类的控件实际实现起来要复杂的很多,因为支持的属性实在的太多了,多动手实践可以帮助理解,希望本文能帮助到在AndroID开发中的大家。

总结

以上是内存溢出为你收集整理的Android中标签容器控件的实例详解全部内容,希望文章能够帮你解决Android中标签容器控件的实例详解所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/1148822.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-31
下一篇 2022-05-31

发表评论

登录后才能评论

评论列表(0条)

保存