第二章 佈局技巧與列表

1 佈局與優化

1.1 五大布局

  • LinearLayout:線性佈局
    • orientation:vertical:垂直的,horizontal:水平的
    • weight:設置比重時,layout_width=”0”。weightSum總比重。例如:微信底部四個tab平分寬帶,可以使其weight 1:1:1:1
  • RelativeLayout:相對佈局
    • xmlns:xml的命名空間
    • layout_alignParentRight:與佈局容器的右邊對齊
    • layout_alignParentBottom:位於父容器的底部
    • layout_alignLeft:與給出id組件的左邊對齊
    • layout_above/toRightOf:給出id組件的上方/右邊
    • layout_marginTop/toRightOf:外邊距
    • layout_paddingRight:內邊距
  • FrameLayout:幀佈局,一層一層疊加起來
  • AbsoluteLayout:絕對佈局
  • TableLayout:表格佈局
    • TableRow

1.2 佈局優化

  • 減少佈局層次的重要性層次過多會影響到性能
    • 官方建議佈局層次–最多10層,所以要減少層次,刪除無用佈局
    • 學會使用相對佈局。佈局結構要清晰,選擇合適的佈局
    • 組合控件
  • 一些有用的屬性
    • 佈局如何引用相同的部分,可以提取出來然後</include>重用佈局文件
    • <merge/>減少視圖層級
    • <ViewStub/>需要是才加載,介紹ViewStub代碼 ,介紹ViewStub文字,兩篇結合來看。ViewStub指向的佈局文件在多數情況下並不會被顯示出來,只會在某種特殊的情況下對ViewStub進行inflate操作,而且這種操作只能進行一次,此時會將ViewStub替換掉,變成顯示所指向的佈局文件。,一旦替換後,此時原來的佈局文件中就沒有ViewStub控件了,這就好像ViewStub只是爲其所指向的佈局佔了一個位子,需要時才顯示
  • 小技巧
    • 不要嵌套多個使用layout_weight屬性的LinearLayout
    • Android lint :刪除無用的資源,佈局,類,引用
    • HierarchyViewer:分析性能及其優化

2 無比重要的ListView

這裏寫圖片描述

  • AdapterView是一個容器,把很多的列表項以合適的形式顯示出來,而列表項的內容和格式可以用Adapter指定,調用AdapterView的setAdapter(Adapter)方法設置Adapter即可。

2.1四種設置Adapter的方法:(以ListView爲例子)

  • 使用ArrayAdapter設置
    1. 告訴大家我要創建列表項了,初始化這個控件
    2. 定義了一個數組,該數組是爲多個列表項提供了數據
    3. 創建ArrayAdapter(Context,textViewResource,數組或者List)對象。其中Context代表訪問應用的接口,一般是this。textViewResource代表一個資源的ID,該資源分ID是指定了每個列表項的組件,等於爲每個列表項指定了格式。後面的數據就按照組件的格式傳入。
    4. 最後爲該ListView設置Adapter
public class ArrayAdapterActivity extends Activity{
    ListView mListView1;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_arrayadapter);
        //1
        mListView1 = (ListView) findViewById(R.id.listview1);
        //2
        String[] arr1 = {"王俊凱" ,"王源","易烊千璽"};
        //3
        ArrayAdapter<String> adapter1 =
                new ArrayAdapter<String>(this,R.layout.array_item,arr1);
        //4
        mListView1.setAdapter(adapter1);    
    }
}
  • 讓Activity繼承ListActivity來實現
    1. 創建ArrayAdapter對象之後,直接使用setListActivity(adapter)設置該窗口顯示列表
public class MainActivity extends ListActivity{
    @override
    public void onCreate(Bundle saveIdnstanceState){
        supert.onCreate(savedInstanceState);
        String[] arr = {"劉詩詩", "趙麗穎", "楊冪"};
        ArrayAdapter<String> adapter1 = new ArrayAdapter<String>(this, R.layout.item, arr);
        setListAdapter(adapter);

    }
}
  • 使用SimpleAdapter
    1. 前面都是創建提供列表內容的數組
    2. 創建一個List集合,集合元素是Map,把前面的內容數組封裝成一個List集合
    3. 創建SimpleAdapter對象。後面有5個參數,第一個是Context。第二個是一個元素是Map的List集合,每個Map對象生成一個列表項。第三個參數是指定界面佈局的ID。第四個是一個String[]對象的參數,決定提取Map中那些key對應的value來生成列表項。第五個參數是對應於value填充的組件,該組件是來源與第三個參數的佈局。是int[]類型的參數
    4. 設置Adapter
public class MainActivity extends AppCompatActivity {
    //1
    private  String[] names = new String[]{"lisa" , "linda" , "july" , "anna"};

    private String[] describe = new String[]{"a cute child" , "a beautiful girl" , "a smart student" , "a handsome boy"};

    private int[] imageId = new int[]{R.drawable.libai,R.drawable.nongyu,R.drawable.qingzhao,R.drawable.tiger};

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //2
        List<Map<String,Object>> listItems = new ArrayList<Map<String, Object>>();

        for (int i = 0 ; i < names.length ; i++){
           Map<String , Object> listItem = new HashMap<String, Object>();
            listItem.put("header" ,imageId[i]);
            listItem.put("name" , names[i]);
            listItem.put("describe" , describe[i]);
            listItems.add(listItem);
        }
        //3
        SimpleAdapter simpleAdapter = new SimpleAdapter(this ,
                listItems ,R.layout.simple_item ,
                new String[]{"header","name","describe"} ,
                new int[]{R.id.header,R.id.name,R.id.desc});

        ListView listView =(ListView) findViewById(R.id.mylist);
        //4
        listView.setAdapter(simpleAdapter);

        Intent intent = getIntent();
        if (intent != null) {
            String string = intent.getStringExtra(SplashActivity.TITLE);
            setTitle(string);
        }
    }
}
  • 擴展BaseAdapter實現 ,需要重寫4個方法
    1. int getCount():返回值控制Adapter會有多少個列表項
    2. Object getItem(int i):返回第i處的列表項的內容
    3. getItemId(int i):返回第i除列表項的id
    4. View getView(int i, View view, ViewGroup viewGroup):返回第i除列表項的組件
 BaseAdapter baseAdapter = new BaseAdapter() {
            @Override
            public int getCount() {
                //指定40個選項
                return 40;
            }

            @Override
            public Object getItem(int i) {
                return null;
            }

            @Override
            public long getItemId(int i) {
                return i;
            }

            @Override
            public View getView(int i, View view, ViewGroup viewGroup) {
                LinearLayout linearLayout = new LinearLayout(MainActivity.this);
                linearLayout.setOrientation(LinearLayout.VERTICAL);
                ImageView imageView = new ImageView(MainActivity.this);
                imageView.setImageResource(R.mipmap.ic_launcher);
                TextView text = new TextView(MainActivity.this);
                text.setText("this is" + i + "listview");
                text.setTextSize(20);
                linearLayout.addView(imageView);
                linearLayout.addView(text);
                return linearLayout;
            }
        };

2.2 優化

  • 不必每次都要加載視圖,只需要第一次的時候加載,所以可以加個判斷
@Override
    public View getView(int i, View view, ViewGroup viewGroup) {
        ViewHolder viewHolder;
        //優化
        if(view == null){
            view = mLayoutInflater.inflate(R.layout.activity_items,null);
            viewHolder = new ViewHolder();
            //獲取控件
            viewHolder.phone_name = (TextView) view.findViewById(R.id.item_name);
            viewHolder.phone_age = (TextView) view.findViewById(R.id.item_age);
            viewHolder.phone_image = (ImageView) view.findViewById(R.id.item_image);
            view.setTag(viewHolder);
        }else {
            viewHolder = (ViewHolder) view.getTag();
        }
        //數據綁定,mInfoList是一個類型爲用戶的List,該用戶有Age和Name屬性
        viewHolder.phone_name.setText(mInfoList.get(i).getmName());
        viewHolder.phone_age.setText(String.valueOf(mInfoList.get(i).getmAge()));
        viewHolder.phone_image.setImageResource(R.mipmap.ic_launcher);
        return view;
    }
  • 設置類來保存所加載視圖的控件
class ViewHolder{
    TextView phone_name;
    TextView phone_age ;
    ImageView phone_image ;
}
  • 所以只有第一次加載的時候,會去加載視圖獲取控件,當前視圖不爲空時,只會去獲取視圖裏控件,節約內存。

2.3 item不同怎麼辦,getItemViewType()

2.4 風格不同的分割線

  • ListView的 android:divider屬性來設置,所以可以用自定義的Drawable來設置分割線的風格

3 GridView和ScrollView

3.1 GridView與ListView的相似之處

  • 需要設置Adapter
  • 它們倆的parent都實現了AdapterView

3.2 GridView的常用實現

  • 常用屬性:
    • android:numColums:設置列數
    • android:horizontalSpacing:設置各元素之間的水平間距
    • android:verticalSpacing:設置各元素之間的垂直間距
    • android:gravity:設置對齊方式
  • 使用了SimpleAdapter,ImageSwitcher是圖片切換器,在切換ImgeView時使用動畫
  • activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:gravity="center_horizontal">

    <GridView
        android:id="@+id/grid01"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:horizontalSpacing="2dp"
        android:verticalSpacing="2dp"
        android:numColumns="4"
        android:gravity="center"/>
    <ImageSwitcher
        android:id="@+id/switcher"
        android:layout_width="300dp"
        android:layout_height="300dp"
        android:layout_gravity="center_horizontal"
        android:inAnimation="@android:anim/fade_in"
        android:outAnimation="@android:anim/fade_out"/>
</LinearLayout>
  • cell.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center_horizontal">

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="50dp"
        android:layout_height="50dp"
        />

</LinearLayout>
  • java源代碼
package com.example.w.myapplication;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.GridView;
import android.widget.ImageSwitcher;
import android.widget.ImageView;
import android.widget.SimpleAdapter;
import android.widget.Toast;
import android.widget.ViewSwitcher;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class MainActivity extends AppCompatActivity {

    GridView gridView;
    ImageSwitcher imageSwitcher;

    int[] imageIds = new int[] {
            R.drawable.bomb5,R.drawable.bomb6,R.drawable.bomb7,R.drawable.bomb8,
            R.drawable.bomb9,R.drawable.bomb10,R.drawable.bomb11,R.drawable.bomb12,
            R.drawable.bomb13,R.drawable.bomb14,R.drawable.bomb15,R.drawable.bomb16,
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        gridView = (GridView)findViewById(R.id.grid01);

        //創建一個List對象,List對象的元素是Map
        List<Map<String,Object>> listItems = new ArrayList<Map<String, Object>>();
        for (int i = 0 ; i < imageIds.length ; i++){
            Map<String,Object> listItem = new HashMap<String, Object>();
            listItem.put("image" , imageIds[i]);
            listItems.add(listItem);
        }
        //爲ImageSwitcher提供一個ViewFactory,由其生成View
        imageSwitcher = (ImageSwitcher)findViewById(R.id.switcher);
        imageSwitcher.setFactory(new ViewSwitcher.ViewFactory() {
            @Override
            public View makeView() {
                //創建ImageView對象
                ImageView imageView = new ImageView(MainActivity.this);
                imageView.setScaleType(ImageView.ScaleType.FIT_CENTER);
                imageView.setLayoutParams(new ImageSwitcher.LayoutParams(
                        ViewGroup.LayoutParams.WRAP_CONTENT,
                        ViewGroup.LayoutParams.WRAP_CONTENT));
                return imageView;
            }
        });
        SimpleAdapter simpleAdapter = new SimpleAdapter(this , listItems ,R.layout.cell
                ,new String[]{"image"},new int[]{R.id.imageView});

        gridView.setAdapter(simpleAdapter);

        //列表被單擊
        gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
                imageSwitcher.setImageResource(imageIds[i]);
                Toast.makeText(MainActivity.this,"圖片" + i +"被點擊了" , Toast.LENGTH_LONG).show();
            }
        });

        //列表被選中
        gridView.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
                imageSwitcher.setImageResource(imageIds[i]);
                Toast.makeText(MainActivity.this,"圖片" + i +"被選中了" , Toast.LENGTH_LONG).show();
            }

            @Override
            public void onNothingSelected(AdapterView<?> adapterView) {

            }
        });
    }
}

3.3 ScrollView

  • 最多隻能包含一個組件,或只支持垂直滾動,若需要水平滾動,則可藉助另一個滾動視圖-HorizontalScrollView

你可看可不看,但我必須得看的Tips:
1. ScrolView瞭解不是很多,需要進一步去了解
2. ListView很重要,和BaseAdapter搭配起來,天生一對
3. 佈局應快要學完了,16.7.23

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