Android自定义控件之流式布局

Android自定义控件之流式布局,第1张

概述效果图:一、首先创建我们的自定义流式布局publicclassFlowLayoutViewextendsViewGroup{publicFlowLayoutView(Contextcontext){this(context,null);}publicFlowLayoutView(Contextcontext,AttributeSetattrs){this(cont

效果图:


一、首先创建我 们的自定义流式布局

public class FlowLayoutVIEw extends VIEwGroup {    public FlowLayoutVIEw(Context context) {        this(context, null);    }    public FlowLayoutVIEw(Context context, AttributeSet attrs) {        this(context, attrs,0);    }    public FlowLayoutVIEw(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);    }    @OverrIDe    protected voID onMeasure(int wIDthMeasureSpec, int heightmeasureSpec) {        super.onMeasure(wIDthMeasureSpec, heightmeasureSpec);    }    @OverrIDe    protected voID onLayout(boolean changed, int l, int t, int r, int b) {            }}


二、在布局文件中使用我们的控件

<com.androIDlongs.flowlayoutvIEwapplication.FlowLayoutVIEw    androID:ID="@+ID/flowlayout"    androID:layout_wIDth="match_parent"    androID:layout_height="match_parent"></com.androIDlongs.flowlayoutvIEwapplication.FlowLayoutVIEw>

三、在 activity中向我们的控件中添加子vIEw四、定义一行对象,来保存每行中所包含的控件信息

class line{        //用来记录当前行的所有TextVIEw        private ArrayList<VIEw> vIEwList = new ArrayList<VIEw>();        //表示当前行所有TextVIEw的宽,还有他们之间的水平间距        private int wIDth;        //当前行的高度        private int height;				}}

五、 在onMeasure方法中完成测量分行 *** 作

 @OverrIDe    protected voID onMeasure(int wIDthMeasureSpec, int heightmeasureSpec) {        super.onMeasure(wIDthMeasureSpec, heightmeasureSpec);        //1.获取FlowLayout的宽度        int wIDth = MeasureSpec.getSize(wIDthMeasureSpec);        //2.计算用于实际比较的宽度,就是wIDth减去左右的padding值        int nopaddingWIDth = wIDth - getpaddingleft()- getpaddingRight();        //3.遍历所有的子TextVIEw,在遍历过程中进行比较,进行分行 *** 作        //创建一个行        line  line = new line();        for (int i = 0; i < getChildCount(); i++) {            final VIEw childAt = getChildAt(i);            //1. 当 当前的行中没有子VIEw,那么直接将子childAt直接放入行中,而不用再比较宽度,要保证            if(line.getVIEwList().size()==0){                line.addlineVIEw(childAt);            }            // 每行至少有一个子TextVIEw                 }    }

可以看到这里有一个addlineVIEw方法,我们需要在line中定义addlineVIEw这个方法

line对象的addlineVIEw方法简析

1.当我们将vIEw添加到line去,我们就需要将子VIEw添加到保存子VIEw的集合(vIEwList)中去,并且要更新line的wIDth,
    
2.那么在更新wIDth的过程中,如果当前添加的子VIEw是当前行中的第一个子控件,那么当前子VIEw的宽就是当前行的宽
    如果不是第一个,则要在当前wIDth的基础上+水平间距+当前添加子VIEw的宽度
3.在这里使用到和水平间距,所以需要定义当前行中的每个子VIEw的水平间距,

private final int DEFAulT_SPACING = 10;		private int horizontalSpacing = DEFAulT_SPACING;//水平间距

4. 通过自定义属性的方式来实现设定水平间距

1. 在values文件夹下新建attrs.xml文件

<?xml version="1.0" enCoding="utf-8"?>					<resources>						<attr name="horizontalSpacing" format="integer"/>						<declare-styleable name="FlowLayoutVIEw">							<attr name="horizontalSpacing"/>						</declare-styleable>					</resources>

2. 在自定义FlowLayoutVIEw的构造方法中获取我们所设定的水平间距

public FlowLayoutVIEw(Context context, AttributeSet attrs, int defStyleAttr) {		super(context, attrs, defStyleAttr);		//获取所有和自定义属性和样式		final TypedArray typedArray = context.gettheme().obtainStyledAttributes(attrs, R.styleable.FlowLayoutVIEw, defStyleAttr, 0);		final int indexCount = typedArray.getIndexCount();		for (int i = 0; i < indexCount; i++) {			final int indexAttr = typedArray.getIndex(i);			switch (indexAttr) {			case R.styleable.FlowLayoutVIEw_horizontalSpacing:				horizontalSpacing = typedArray.getInt(indexAttr,10);				break;					 }				}			typedArray.recycle();		}


5. 通过向外暴露一个方法来设置

/** * 设置子VIEw直接的水平间距 * @param horizontalSpacing*/public voID setHorizontalSpacing(int horizontalSpacing){		if(horizontalSpacing>0){		this.horizontalSpacing = horizontalSpacing;		}	}

6. 添加子vIEw后,更新当前行的高度,实际上在这里,每个子VIEw的高度就是当前行的高度

public voID addlineVIEw(VIEw lineVIEw){			if(!vIEwList.contains(lineVIEw)){				vIEwList.add(lineVIEw);								//更新wIDth				if(vIEwList.size()==1){					//如果是第一个TextVIEw,那么wIDth就是lineVIEw的宽度					wIDth = lineVIEw.getMeasureDWIDth();				}else {					//如果不是第一个,则要在当前wIDth的基础上+水平间距+lineVIEw的宽度					wIDth += horizontalSpacing + lineVIEw.getMeasureDWIDth();				}				//更新height,在此所有的TextVIEw的高度都是一样的				height = Math.max(height,lineVIEw.getMeasuredHeight());			}			}

分行逻辑简析

  当当前行中已经有了子vIEw后,当前行的宽度+水平间距+当前添加的子VIEw的宽度大于控件的宽度的时候,需要进行换行 *** 作,那么在换行 *** 作之前,需要先保存之前的行,所以这里使用集合来进行保存

private ArrayList<line> lineList = new ArrayList<FlowLayout.line>();

  当前行的宽度+水平间距+当前添加的子VIEw的宽度不大于控件的宽度的时候,直接将当前子VIEw添加到当前行中去

//遍历所有的子TextVIEw,进行分行 *** 作		line line = new line();//只要不换行,始终都是同一个line对象		for (int i = 0; i<getChildCount(); i++) {		//获取子TextVIEw			VIEw childVIEw = getChildAt(i);			//引起vIEw的onMeasure方法回调,从而保证后面的方法能够有值			childVIEw.measure(0,0);						//如果当前line中 没有TextVIEw,则直接放入当前line中			if(line.getVIEwList().size()==0){				line.addlineVIEw(childVIEw);			}else if(line.getWIDth()+horizontalSpacing+childVIEw.getMeasureDWIDth()>nopaddingWIDth) {				//如果当前line的宽+水平间距+childVIEw的宽大于nopaddingWIDth,则换行				lineList.add(line);//先保存之前的line对象								line = new line();//重新创建line				line.addlineVIEw(childVIEw);//将chIDlVIEw放入新的line			}else {				//如果小于nopaddingWIDth,则将childVIEw放入当前line中				line.addlineVIEw(childVIEw);			}						//7.如果当前childVIEw是最后一个,那么就会造成最后的一个line对象丢失,			if(i==(getChildCount()-1)){				lineList.add(line);//保存最后的line对象			}		}


设定控件加载的高度

//for循环结束后,lineList就存放了所有的line对象,而每个line中有记录自己的所有TextVIEw        //为了能够垂直的摆放所有的line的TextVIEw,所以要给当前FlowLayout设置对应的宽高,        //计算所需要的高度:上下的padding + 所有line的高度   + 所有line之间的垂直间距        int height = getpaddingtop()+getpaddingBottom();        for (int i = 0; i < lineList.size(); i++) {            height += lineList.get(i).getHeight();        }        height += (lineList.size()-1)*verticalSpacing;

所以这里使用到了所有行之间的垂直间距,这里可以定义自定属性来设置,也可以设定一个调用方法来进行设置

//行与行之间的垂直间距    private int verticalSpacing = 10;    /**     * 设置行与行之间的垂直间距     * @param verticalSpacing     */    public voID setVerticalSpacing(int verticalSpacing){        if(verticalSpacing>0){            this.verticalSpacing = verticalSpacing;        }    }

六、 在onLayout方法中进行摆放 *** 作

1.摆放所有的行的时候,我们需要循环取出每一个行line,其次我们再获取每个line中的所有的子vIEw,然后再获取每一个子vIEw
2.再摆放每一行中的第一个子vIEw,其次再摆放当前行中的第二个以后的子vIEw,在排放后面的子vIEw的时候,需要参考前面的子vIEw

/**	 * 摆放 *** 作,让所有的子TextVIEw摆放到指定的位置上面	 */	@OverrIDe	protected voID onLayout(boolean changed, int l, int t, int r, int b) {		int paddingleft = getpaddingleft();		int paddingtop = getpaddingtop();		for (int i = 0; i < lineList.size(); i++) {			line line = lineList.get(i);//获取line对象						//从第二行开始,他们的top总是比上一行多一个行高+垂直间距			if(i>0){				paddingtop += lineList.get(i-1).getHeight()+verticalSpacing;			}			ArrayList<VIEw> vIEwList = line.getVIEwList();//获取line所有的TextVIEw			//1.计算出当前line的留白区域的值			int remainSpacing = getlineRemainSpacing(line);			//2.计算每个TextVIEw分到多少留白			float perSpacing = remainSpacing/vIEwList.size();						for (int j = 0; j < vIEwList.size(); j++) {				VIEw childVIEw = vIEwList.get(j);//获取每个TextVIEw				//3.将perSpacing增加到每个TextVIEw的宽度上				int wIDthMeasureSpec = MeasureSpec.makeMeasureSpec((int) (childVIEw.getMeasureDWIDth()+perSpacing),MeasureSpec.EXACTLY);				childVIEw.measure(wIDthMeasureSpec,0);								if(j==0){					//摆放每行的第一个TextVIEw					childVIEw.layout(paddingleft,paddingtop,paddingleft+childVIEw.getMeasureDWIDth()							,paddingtop+childVIEw.getMeasuredHeight());				}else {					//摆放后面的TextVIEw,需要参照前一个VIEw					VIEw preVIEw = vIEwList.get(j-1);					int left = preVIEw.getRight()+horizontalSpacing;					childVIEw.layout(left,preVIEw.gettop(),left+childVIEw.getMeasureDWIDth(), 							preVIEw.getBottom());				}			}		}	}

当一行中放不下下一个控件,但是还有很宽的空白处,这时候,我们需要将这段空白计算出来,然后将这段空白平均分配给当前行中的每个子vIEw

/**	 * 获取line的留白区域	 * @param line	 * @return	 */	private int getlineRemainSpacing(line line){		return getMeasureDWIDth()-getpaddingleft()-getpaddingRight()-line.getWIDth();	}


七、在activity中向布局中添加子VIEw

 final FlowLayoutVIEw vIEwByID = (FlowLayoutVIEw) findVIEwByID(R.ID.framlayout);        for (int i = 0; i < mStringArray.length; i++) {                final TextVIEw textVIEw = new TextVIEw(this);                textVIEw.setText(mStringArray[i]);                textVIEw.setTextcolor(color.BLUE);                textVIEw.setGravity(Gravity.CENTER);                textVIEw.setTextSize(16);                textVIEw.setpadding(15, 15, 15, 15);                vIEwByID.addVIEw(textVIEw);            }

其中mStringArray是一个保存了String的字符串数组

效果图比较不好看

然后我们可以设置下子vIEw的样式背景

 Drawable normal = generateDrawable(randomcolor(), 10);            Drawable pressed = generateDrawable(randomcolor(), 10);            textVIEw.setBackgroundDrawable(generateSelector(pressed, normal));

这里是我们在java代码中动态创建状态选择器,其中文字的背景是随机生成 的

 public static StateListDrawable generateSelector(Drawable pressed,Drawable normal){        StateListDrawable drawable = new StateListDrawable();        drawable.addState(new int[]{androID.R.attr.state_pressed}, pressed);//设置按下的图片        drawable.addState(new int[]{}, normal);//设置默认的图片        return drawable;    }        public  GradIEntDrawable generateDrawable(int argb,float radius){        GradIEntDrawable drawable = new GradIEntDrawable();        drawable.setShape(GradIEntDrawable.RECTANGLE);//设置为矩形,默认就是矩形        drawable.setCornerRadius(radius);//设置圆角的半径        drawable.setcolor(argb);        return drawable;    }    /**     * 随机生成漂亮的颜色     * @return     */    public  int randomcolor(){        Random random = new Random();        //如果值太大,会偏白,太小则会偏黑,所以需要对颜色的值进行范围限定        int red = random.nextInt(150)+50;//50-199        int green = random.nextInt(150)+50;//50-199        int blue = random.nextInt(150)+50;//50-199        return color.rgb(red, green, blue);//根据rgb混合生成一种新的颜色    }



点击下载本节源码

访问密码:4vrl

 AndroID自定义控件ImageViwe(一)——依据控件的大小来设置缩放图片显示
   点击打开链接
    
 AndroID自定义ImageVIEw(二)——实现双击放大与缩小图片
   点击打开链接
    
 AndroID自定义控件ImageViwe(三)——随手指进行图片的缩放
    点击打开链接
    
 AndroID自定义控件ImageViwe(四)——多点触控实现图片的自由移动  
    点击打开链接
    
 AndroID ListVIEw分组排序显示数据
    点击打开链接
    
 AndroID自定义下拉刷新功能的ListVIEw
   点击打开链接
    
 AndroID音乐播放器高级开发
   点击打开链接
   

本章智力解答:  在一个房间里,有油灯 ,暖炉及壁炉。现在,想要用一根火柴将三个器具点燃,请问首先应该点燃哪一个?

都不是 ,应当先点火柴,只有点燃了火柴才能去点其他东西



总结

以上是内存溢出为你收集整理的Android自定义控件之流式布局全部内容,希望文章能够帮你解决Android自定义控件之流式布局所遇到的程序开发问题。

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

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

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

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

发表评论

登录后才能评论

评论列表(0条)

保存