ACRA : Application crash report for android
- 作用: 为自己的应用找bug
- 使用步骤:参考文档
自定义布局的实现:流程图
参照流程图:当有孩子时,是否需要对孩子控件大小进行布置,如果需要就得重写onMeasure()这个方法调用child.layout()方法。需要孩子控件布局进行控制也要重写onLayout()方法,需要对控件的显示进行控制时要重写onDraw()方法。
一般实现全部构造函数。
重写
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b)
{
//临时top
int topmt=0;
int parentWidth = getMeasuredWidth();
//获得孩子个数
int count =getChildCount();
for(int i=0;i<count;i++){
//获得孩子实例
View child=getChildAt(i);
//获得孩子的高度、宽度
int childHidth=child.getMeasuredHeight();
int childWidth=child.getMeasuredWidth();
if(i%2==0){ //双行
int left=parentWidth-childWidth;
int top=topmt;
int right=left+childWidth;
int bottom=top+childHidth;
child.layout(left, top, right, bottom);
}else{//单行
int left=0;
int top=topmt;
int right=left+childWidth;
int bottom=top+childHidth;
child.layout(left, top, right, bottom);
}
topmt+=childHidth;
}
}
还需要重写onMeasure()
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
//获得父亲的宽高
int widthsize=MeasureSpec.getSize(widthMeasureSpec);
int heightsize=MeasureSpec.getSize(heightMeasureSpec);
measureChildren(0, 0);//设置孩子,为0由父亲安排宽高
setMeasuredDimension(widthsize, heightsize);
}
自定义布局的使用:
<com.cca.definelayout.CustomerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:id="@+id/customer_layout"
android:layout_height="match_parent"
tools:context="com.cca.definelayout.MainActivity" >
<View
android:layout_width="220dp"
android:layout_height="40dp"
android:background="#ff0000"
/>
<View
android:layout_width="220dp"
android:layout_height="40dp"
android:background="#00ff00"
/>
<View
android:layout_width="220dp"
android:layout_height="40dp"
android:background="#fe3300"
/>
<View
android:layout_width="220dp"
android:layout_height="40dp"
android:background="#ff0220"
/>
<View
android:layout_width="220dp"
android:layout_height="40dp"
android:background="#ffdd00"
/>
</com.cca.definelayout.CustomerLayout>
如果需要切换布局,一般定义一个boolean类型值进行切换
FlowLayout
- 分析:
- 多行摆放
- 单行如果摆不下去,换行摆放
看下效果图:
一下子看到这种布局说真的我无从下手,不知道这个怎么实现,每行个数不一样,列数不一样,后来知道自定义布局可以实现。根据上面的view实现的流程图可以知道,需要对控件的布局、大小进行控制,所以需要重写onLayout()、onMeasure()的方法。再重写方法之前,需要先分析每一行是怎么布局的,以面向对象的思想封装每一行需要的属性、和方法。如下:
行的类
class Line{
//存储孩子
private List<View> mChildViews=new LinkedList<View>();
private int usedWidth;//已经使用过的宽度
private int lineHeight;//行最大的高度
private int maxWidth;//行最大的宽度,父类给的
private int horizontalSpace;//中间的间隔
public Line(int maxWidth,int horizontalSpace){
this.maxWidth=maxWidth;
this.horizontalSpace=horizontalSpace;
}
//判断该行是否能添加view
public boolean canAddView(View view){
//如果使用的宽度+准备加的View的宽度+中间的间隔>最大的宽度,加不上去
//准备加的View的宽度
int childwidth=view.getMeasuredWidth();
int size=mChildViews.size();
if(size==0){
return true;
}else if(usedWidth+childwidth+horizontalSpace>maxWidth){
return false;
}
return true;
}
//添加view到布局
public void addView(View view){
//
int childWidth=view.getMeasuredWidth();
int childHeight=view.getMeasuredHeight();
int size=mChildViews.size();
if(size==0){
//没有孩子 已经使用的宽度
if(childWidth>maxWidth){
usedWidth=maxWidth;
}else{
usedWidth=childWidth;
}
//高度
lineHeight=childHeight;
}else{
//已经使用的宽度
usedWidth=usedWidth+childWidth+horizontalSpace;
//高度
lineHeight=lineHeight>childHeight?lineHeight:childHeight;
}
//加孩子
mChildViews.add(view);
}
//给行布局
public void layout(int left,int top)
{
//给孩子布局
int size=mChildViews.size();
int tmpLeft=0;
//将每一行右侧无法显示的空白部分平分给每一行显示的每个控件
int extraWidth=(int) ((maxWidth-usedWidth)*1f/size+0.5f);
for(int i=0;i<size;i++){
View child=mChildViews.get(i);
int childWidth=child.getMeasuredWidth();
int childHeight=child.getMeasuredHeight();
if(extraWidth>0){
//希望孩子再宽点,填充右侧空白
int widthMeasureSpec=MeasureSpec.makeMeasureSpec(childWidth+extraWidth, MeasureSpec.EXACTLY);
int heightMeasureSpec=MeasureSpec.makeMeasureSpec(childHeight,MeasureSpec.EXACTLY);
child.measure(widthMeasureSpec, heightMeasureSpec);
//重新获得宽高
childWidth=child.getMeasuredWidth();
childHeight=child.getMeasuredHeight();
}
int extraHeight=(int) ((lineHeight-childHeight)/2f+0.5f);
int l=left+tmpLeft;
int t=top+extraHeight;
int r=l+childWidth;
int b=t+childHeight;
child.layout(l, t, r, b);
//添加记录
tmpLeft +=childWidth + horizontalSpace;
}
}
}
重写onLayout()方法:
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b)
{
int left=getPaddingLeft();
int top=getPaddingTop();
//让行进行布局
for(int i=0;i<mLines.size();i++){
Line line=mLines.get(i);
//给行布局
line.layout(left,top);
//添加top的记录
top +=line.lineHeight;
if(i!=mLines.size()-1){
top +=mVertivalSpace;
}
}
}
重写onMeasure()方法
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
//孩子个数记录清空
mLines.clear();
mCurrentLine=null;
int widthSize=MeasureSpec.getSize(widthMeasureSpec);
int lineMaxWidth=widthSize-getPaddingLeft()-getPaddingRight();
//测量孩子完成时,就记录到行里面
int count=getChildCount();
for(int i=0;i<count;i++){
View child=getChildAt(i);
//孩子不可见时
if(child.getVisibility()==View.GONE){
continue;
}
//给孩子宽高赋值,父亲给的最大宽高
measureChild(child, widthMeasureSpec, heightMeasureSpec);
//将孩子添加到行中
if(mCurrentLine==null){
//新建行
mCurrentLine=new Line(lineMaxWidth,mHorizontalSpace);
//添加到布局中
mLines.add(mCurrentLine);
}
//给行添加孩子
if(mCurrentLine.canAddView(child)){
//可以添加孩子
mCurrentLine.addView(child);
}else{
//加不了
//换行
mCurrentLine=new Line(lineMaxWidth,mHorizontalSpace);
//添加到布局中
mLines.add(mCurrentLine);
//再加孩子
mCurrentLine.addView(child);
}
}
//设置自己的宽高
int measuredWidth=widthSize;
int measuredHeight=getPaddingTop()+getPaddingBottom();
//通过line的高来计算自己的高度
for(int i=0;i<mLines.size();i++){
Line line=mLines.get(i);
measuredHeight +=line.lineHeight;
if(i!=0){
measuredHeight +=mVertivalSpace;
}
}
setMeasuredDimension(measuredWidth, measuredHeight);
}
在MainActivity中:
public class MainActivity extends Activity {
private FlowLayout layout;
private String []mDatas={"单机游戏","美女","游戏","单机游戏","美女","淘宝","单机游戏","美女","淘宝","游戏" ,"单机游戏","淘宝","游戏","单机游戏","美女","淘宝","游戏","单机游戏","美女","淘宝","游戏" ,"有道","天猫","汽车商城","新闻","运动","熊出没之大逃跑"};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
layout=(FlowLayout) findViewById(R.id.flow_layout);
layout.setPadding(10, 10, 10, 10);
initData();
}
private void initData()
{
for(int i=0;i<mDatas.length;i++){
TextView tv=new TextView(this);
tv.setText(mDatas[i]);
tv.setTextColor(Color.WHITE);
tv.setGravity(Gravity.CENTER);
tv.setBackgroundColor(Color.GRAY);
tv.setPadding(3, 3, 3, 3);
layout.addView(tv);
}
}
}
使用自定义布局时:
<ScrollView 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="com.cca.frowlayout.MainActivity" >
<com.cca.frowlayout.FlowLayout
android:id="@+id/flow_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</ScrollView>
到这里,上图的效果就已经出来,这种布局的逻辑判断有点繁琐,稍有差错就显示不出来了。记得要多看。