ACRA 和自定義佈局

ACRA : Application crash report for android

  1. 作用: 爲自己的應用找bug
  2. 使用步驟:參考文檔

自定義佈局的實現:流程圖

參照流程圖:當有孩子時,是否需要對孩子控件大小進行佈置,如果需要就得重寫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

  1. 分析:
    1. 多行擺放
    2. 單行如果擺不下去,換行擺放

看下效果圖:

一下子看到這種佈局說真的我無從下手,不知道這個怎麼實現,每行個數不一樣,列數不一樣,後來知道自定義佈局可以實現。根據上面的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>

到這裏,上圖的效果就已經出來,這種佈局的邏輯判斷有點繁瑣,稍有差錯就顯示不出來了。記得要多看。

發佈了40 篇原創文章 · 獲贊 9 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章