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