(三) UI開發 Part 3 RecyclerView

RecyclerViewjava

1)RecyclerView的基本用法android

2)橫向滾動和瀑布流滾動閉包

3)註冊點擊事件app

3.6 強大的滾動控件 RecyclerViewdom

ListView缺點:ide

1.不使用技巧優化,ListView效率不好。函數

2.擴展性能不夠好,只能實現數據縱向滾動。佈局

3.6.1 RecyclerView的基本用法性能

1.RecylerView定義在了support庫當中。gradle

首先須要在build.gradle中添加相應的依賴庫,在dependencies閉包中添加內容。

    compile 'com.android.support:appcompat-v7:24.2.1'
    compile 'com.android.support:recyclerview-v7:24.2.1'
    testCompile 'junit:junit:4.12'

2.修改main_layout.xml中的代碼

在佈局中加入RecylerView。因爲RecyclerView並非內置在系統SDK中的,因此須要把完整的路徑寫出來。

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        />

</LinearLayout>

3.建立Hero類和hero_item.xml(同ListView)

public class Hero {
    private String name;
    private int imageId;
    public Hero(String name, int imageId){
        this.name = name;
        this.imageId = imageId;
    }

    public String getName() {
        return name;
    }

    public int getImageId() {
        return imageId;
    }
}
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:id="@+id/hero_image"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <TextView
        android:id="@+id/hero_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:layout_marginLeft="10dp"/>
</LinearLayout>

4.新建HeroAdapter類,讓這個適配器繼承自RecyclerView.Adapter,並將泛型指定爲HeroAdapter.ViewHolder,ViewHolder是在HeroAdapter中定義的一個內部類。

public class HeroAdapter extends RecyclerView.Adapter<HeroAdapter.ViewHolder>{

    private List<Hero> mHeroList;

    static class ViewHolder extends RecyclerView.ViewHolder{
        ImageView heroImage;
        TextView heroName;

        public ViewHolder(View view){
            super(view);
            heroImage = (ImageView)view.findViewById(R.id.hero_image);
            heroName = (TextView)view.findViewById(R.id.hero_name);
        }
    }

    public HeroAdapter(List<Hero> heroList){
        mHeroList = heroList;
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.hero_item, parent, false);
        ViewHolder holder = new ViewHolder(view);
        return holder;
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        Hero hero = mHeroList.get(position);
        holder.heroImage.setImageResource(hero.getImageId());
        holder.heroName.setText(hero.getName());
    }

    @Override
    public int getItemCount() {
        return mHeroList.size();
    }
}

首先定義一個內部類ViewHolder,ViewHolder要繼承自RecylcerView.ViewHolder。ViewHolder的構造函數中傳入的View參數,一般爲RecylclerView子項的最外層佈局。能夠經過findVIewById()方法來獲取到佈局中的ImageView和TextView的實例。

HeroAdapter中的構造函數,將要展現的數據源傳進來,並賦值給一個全局變量mHeroList,後續操做都在該數據源的基礎上進行。

因爲HeroAdapter繼承了RecyclerView.Adapter,那麼就必須重寫onCreatViewHolder(),onBindViewHolder()和getItemCount()三個方法。

onCreatViewHolder()方法用於建立ViewHolder實例,在這個方法中將hero_item佈局傳進來,而後建立一個VIewHolder實例,將加載出來的佈局傳入到構造函數中,最後將ViewHolder實例返回。

 @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.hero_item, parent, false);
        ViewHolder holder = new ViewHolder(view);
        return holder;
    }

onBindViewHolder()方法是用於對RecyclerView子項的數據進行賦值,會在每一個子項被滾動到屏幕內的時候執行,經過position參數獲得當前項的Hero實例,而後再將數據置入ViewHolder的ImageView和TextView。

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        Hero hero = mHeroList.get(position);
        holder.heroImage.setImageResource(hero.getImageId());
        holder.heroName.setText(hero.getName());
    }

getItemCount()方法用於告訴RecycleView一共有多少子項,直接返回數據源的長度。

 @Override
    public int getItemCount() {
        return mHeroList.size();
    }

5.修改MainActivity中的代碼

public class MainActivity extends AppCompatActivity {

    private List<Hero> heroList = new ArrayList<Hero>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.main_layout);
        initHeros();
        RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
        LinearLayoutManager layoutManager = new LinearLayoutManager(this);
        recyclerView.setLayoutManager(layoutManager);
        HeroAdapter adapter = new HeroAdapter(heroList);
        recyclerView.setAdapter(adapter);
    }

    private void initHeros(){
        for(int i = 0 ; i < 2 ; i ++){
            Hero gareen = new Hero("gareen", R.drawable.gareen);
            heroList.add(gareen);
            Hero annie = new Hero("annie", R.drawable.annie);
            heroList.add(annie);
            Hero shana = new Hero("shana", R.drawable.shana);
            heroList.add(shana);
            Hero teemo = new Hero("teemo", R.drawable.teemo);
            heroList.add(teemo);
        }
    }
}

用一樣的initHeros()方法,初始化全部英雄數據。

在onCreat()方法中先獲取到RecyclerView的實例,而後建立一個LinearLayoutManager對象,並設置到RecyclerView當中。LayoutManager用於指定RecyclerView的佈局方式,這裏使用LinearLayoutManager爲線性佈局。而後建立HeroAdapter的實例,並將英雄數據傳入HeroAdapter的構造函數中,最後調用RecycleView的setAdapter()方法來完成適配器設置,完成RecyclerView和數據之間的關聯。

 

 

3.6.2 實現橫向滾動和瀑布流滾動

1.橫向滾動

a.首先對hero_item的佈局進行修改,目前佈局裏的元素水平放置的,即爲horizontal,把元素改成垂直放置,寬度設爲100dp

ImageView和TextView都設置爲在佈局中水平居中,並使用layout_marginTop讓文字和圖片之間保持必定距離

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="100dp"
    android:layout_height="wrap_content">

    <ImageView
        android:id="@+id/hero_image"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" android:layout_gravity="center_horizontal"
        />

    <TextView
        android:id="@+id/hero_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:layout_marginLeft="10dp"/>
</LinearLayout>

 b.修改MainActivity中的代碼

@Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.main_layout);
        initHeros();
        RecyclerView recyclerView = (RecyclerView) f 
        indViewById(R.id.recycler_view);
        LinearLayoutManager layoutManager = new LinearLayoutManager(this);
        layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
        recyclerView.setLayoutManager(layoutManager);
        HeroAdapter adapter = new HeroAdapter(heroList);
        recyclerView.setAdapter(adapter);
    }

加一句

layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);

調用LinearLayoutManager的setOri()方法來設置佈局的排列方向,默認爲縱向,傳入LinearLayoutManger.HORIZEONTAL表示讓佈局橫向排列。

運行程序:

緣由:ListView的佈局排列是由自身去管理的,而RecylerView則由LayoutManager管理,LayoutManager中制定了一套可擴展的佈局排列接口,子類只要按照接口的規範來實現,就能定製出各類不一樣的排列方式的佈局。

2.瀑布流佈局

a.修改hero_item.xml中的佈局,寬度由列數自動匹配而不是一個固定值,用layout_margin來讓子項之間互留一點間距,TextView的對齊屬性改成了左對齊,由於待會會將文字的長度邊長,居中顯示很奇怪

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_margin="5dp">

    <ImageView
        android:id="@+id/hero_image"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        />

    <TextView
        android:id="@+id/hero_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="left"
        android:layout_marginLeft="10dp"/>
</LinearLayout>

b.修改MainActivit.java中的代碼

在onCreat()方法中建立了一個StaggeredGridLayoutManager的實例,構造函數接受兩個參數,第一個參數用於指定佈局的列數,第二個參數用於制定佈局的排列方向,傳入StaggeredGridLayoutManager.VERTICAL表示會讓佈局縱向排列。

在getRandomLengthName()方法中獲得每一個英雄名字都長短不一,顯示出來的效果更明顯

public class MainActivity extends AppCompatActivity {

    private List<Hero> heroList = new ArrayList<Hero>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.main_layout);
        initHeros();
        RecyclerView recyclerView = (RecyclerView)     
        findViewById(R.id.recycler_view);
        StaggeredGridLayoutManager layoutManager = new       
        StaggeredGridLayoutManager(3,StaggeredGridLayoutManager.VERTICAL);
        recyclerView.setLayoutManager(layoutManager);
        HeroAdapter adapter = new HeroAdapter(heroList);
        recyclerView.setAdapter(adapter);
    }

    private void initHeros(){
        for(int i = 0 ; i < 3 ; i ++){
            Hero gareen = new Hero(getRandomLengthName("gareen"), R.drawable.gareen);
            heroList.add(gareen);
            Hero annie = new Hero(getRandomLengthName("annie"), R.drawable.annie);
            heroList.add(annie);
            Hero shana = new Hero(getRandomLengthName("shana"), R.drawable.shana);
            heroList.add(shana);
            Hero teemo = new Hero(getRandomLengthName("teemo"), R.drawable.teemo);
            heroList.add(teemo);
        }
    }

    private String getRandomLengthName(String name){
        Random random = new Random();
        int length = random.nextInt(20) + 1 ;
        StringBuilder res = new StringBuilder();
        for(int i = 0 ; i < length ; i ++){
            res.append(name);
        }
        return res.toString(); }
}        

運行程序

3.6.3 RecycleView的點擊事件

ListView中的setOnClickListener()方法註冊的是子項的點擊事件,若是是點擊子項裏的按鈕,則實現起來很麻煩。

RecyclerView摒棄了子項點擊事件的監聽器,全部監聽事件都由具體的View來註冊。

在RecyclerView中註冊點擊事件,修改MainActivity中的代碼

public class HeroAdapter extends RecyclerView.Adapter<HeroAdapter.ViewHolder>{

    private List<Hero> mHeroList;

    static class ViewHolder extends RecyclerView.ViewHolder{
        View heroView;
        ImageView heroImage;
        TextView heroName;

        public ViewHolder(View view){
            super(view);
            heroView = view;
            heroImage = (ImageView)view.findViewById(R.id.hero_image);
            heroName = (TextView)view.findViewById(R.id.hero_name);
        }
    }

    public HeroAdapter(List<Hero> heroList){
        mHeroList = heroList;
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.hero_item, parent, false);

        final ViewHolder holder = new ViewHolder(view);
        holder.heroView.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v) {
                int position = holder.getAdapterPosition();
                Hero hero = mHeroList.get(position);
                Toast.makeText(v.getContext() , "You clicked view " + hero.getName(), Toast.LENGTH_SHORT ).show();
            }
        });
        holder.heroImage.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v) {
                int position = holder.getAdapterPosition();
                Hero hero = mHeroList.get(position);
                Toast.makeText(v.getContext() , "You clicked image " + hero.getName(), Toast.LENGTH_SHORT).show();
            }
        });

        return holder;
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        Hero hero = mHeroList.get(position);
        holder.heroImage.setImageResource(hero.getImageId());
        holder.heroName.setText(hero.getName());
    }

    @Override
    public int getItemCount() {
        return mHeroList.size();
    }
}

在ViewHolder中添加了heroView變量用來保存子項最外層佈局的實例,而後在onCreatViewHolder()方法中註冊事件。

運行程序:

點擊圖片部分:

顯示點擊了圖片

點擊圖片下的TextView部分:因爲TextView並無註冊時間,因此該事件會被子項最外層佈局捕捉到。

 最後一點小小的總結:

1.線性佈局中orientation屬性默認爲horizontal(水平方向)

2.android:gravity用於指定文字在控件中的對齊方式,android:layout_gravity用於指定控件在佈局中的對齊方式。

3.將控件寬度指定爲0dp,用android:layout_weight來決定控件在屏幕的比例寬度

<EditText
     android:id="@+id/input_message" android:layout_width="0dp"
     android:layout_height="wrap_content" android:layout_weight="1" />
<Button
     android:id="@+id/button" android:layout_width="0dp"
     android:layout_height="wrap_content"
     android:layout_weight="1" android:text="Button"/>

表示EditText和Button以1:1的比例平分屏幕,以此類推。

4.動態加載佈局的LayoutInflater方法,LayoutInflater的from()方法能夠構建出一個LayouInflater對象,而後調用inflate()方法就能夠加載一個佈局文件。

inflate()方法接受兩個參數,第一個參數要加載的佈局文件的id,第二個參數是給加載好的佈局再添加一個父佈局。

接收三個參數,第三個爲boolean型,false表示只讓在父佈局中聲明的layout屬性生效,但不會爲View添加父佈局。

ListView中的標準寫法爲:

View view = LayoutInflater.from(getContext()).inflate(resourceId, parent, false);
相關文章
相關標籤/搜索