1. 程式人生 > >【RecyclerView】Android 橫豎屏切換 超便捷解決方案

【RecyclerView】Android 橫豎屏切換 超便捷解決方案

相信Android 開發者大多數都遇到過橫豎屏切換的需求,往往最後選擇了鎖定豎屏,或者鎖定橫屏的體驗。或者每次切換螢幕就要切換顯示資料的控制元件。谷歌官方推出的RecyclerView控制元件讓你不再有這樣的煩惱。

最近這兩天又不是很忙了,閒下來看了些關於5.0的一些特性,比如材料設計規範、陰影、主題等等,還有RecyclerView和CardView這兩個不錯的控制元件。看到RecyclerView的時候感覺眼前一亮,毫無疑問,它能夠完全勝任ListView,GridView以及現在流行的瀑布流(交錯佈局)顯示,並且有著高度解耦,高自由度(自定義分隔線,自定義載入/刪除動畫等)等特點。

在這裡,我用它寫了一個橫豎屏切換便捷解決方案的Demo。

本demo於API 23 + eclipse環境中完成(但本demo相容低版本,如下載demo可以修改最低版本)

1.獲取RecyclerView的jar包

RecyclerView雖然是谷歌官方推出的控制元件,但是為了相容5.0以前的系統版本,它是屬於V7相容包下獨立打包的。所以如果我們要使用RecyclerView的話需要匯入這個jar包。

更新SDK Manager 中Extras的support library到21+版本後,其jar包在如下位置:
sdk\extras\android\support\v7\recyclerview\libs\android-support-v7-recyclerview.jar

如果不方便更新sdk的話,點選jar包下載地址

匯入專案並add to build path。

2.Activity中橫豎屏切換控制

OrientationChangeActivity.java

/**
 * @author 塵笑ys
 *  RecyclerView 橫豎屏切換demo
 */
public class OrientationChangeActivity extends Activity {

    private RecyclerView recyclerView;
    private ArrayList<String> datas = new
ArrayList<String>(); private LinearLayoutManager mLlayoutmanager; private GridLayoutManager mGlayoutmanager; CharAdapter charad; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); recyclerView = (RecyclerView) findViewById(R.id.id_recyclerview); //切換螢幕時儲存資料,如有資料,則不再進行資料的初始化 if (savedInstanceState != null && savedInstanceState.getStringArrayList("data") != null) { datas = savedInstanceState.getStringArrayList("data"); }else { initData(); } //豎屏線性展示 mLlayoutmanager = new LinearLayoutManager(this); //橫屏每行顯示3個 mGlayoutmanager = new GridLayoutManager(this,3); reviewonScreenChanged(getResources().getConfiguration()); recyclerView.setAdapter(charad = new CharAdapter(this, datas)); charad.setOnItemClickLitener(new OnItemClickLitener() { @Override public void onItemLongClick(View view, int position) { //這裡要先使用這個資料再刪除,因為一旦刪除,就獲取不到這個資料了,獲取的就是下一個資料了 Toast.makeText(OrientationChangeActivity.this, datas.get(position)+'\t'+"delete...", Toast.LENGTH_SHORT).show(); charad.removeData(position); } @Override public void onItemClick(View view, int position) { Toast.makeText(OrientationChangeActivity.this, datas.get(position), Toast.LENGTH_SHORT).show(); } }); } private void reviewonScreenChanged(Configuration newConfig) { if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) { //橫屏 recyclerView.setLayoutManager(mGlayoutmanager); }else { //豎屏 recyclerView.setLayoutManager(mLlayoutmanager); } } @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); reviewonScreenChanged(newConfig); } private void initData() { for (int i = 'A'; i <= 'z'; i++) { datas.add((char)i + ""); } } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putStringArrayList("data", datas); } }

3.RecyclerView.Adapter

CharAdapter.java

/**
 * @author 塵笑ys
 *
 */
public class CharAdapter extends RecyclerView.Adapter<CharAdapter.MyViewHolder>{
//型別用自定義的ViewHolder

    private List<String> mDatas;
    private LayoutInflater mInflater;

    //RecyclerView目前需要自己來定義介面
    public interface OnItemClickLitener
    {
        //點選事件
        void onItemClick(View view, int position);
        //長按事件
        void onItemLongClick(View view , int position);
    }

    private OnItemClickLitener mOnItemClickLitener;

    //設定監聽的方法,注意引數是CharAdapter.OnItemClickLitener而不是View.OnItemClickLitener
    public void setOnItemClickLitener(OnItemClickLitener mOnItemClickLitener)
    {
        this.mOnItemClickLitener = mOnItemClickLitener;
    }

    //構造時傳入資料,如果考慮資料解耦,可將資料賦值放到setData()中
    public CharAdapter(Context context, List<String> datas)
    {
        mInflater = LayoutInflater.from(context);
        mDatas = datas;
    }

    @Override
    //檢視快取建立
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
    {
        //檢視快取,在ViewHolder構造時獲取檢視控制元件
        MyViewHolder holder = new MyViewHolder(mInflater.inflate(
                R.layout.item_char, parent, false));
        return holder;
    }

    @Override
    //檢視快取的資料繫結
    public void onBindViewHolder(final MyViewHolder holder, final int position)
    {
        holder.tv.setText(mDatas.get(position));

        // 如果設定了回撥,則設定點選事件
        if (mOnItemClickLitener != null)
        {
            holder.itemView.setOnClickListener(new OnClickListener()
            {
                @Override
                public void onClick(View v)
                {
                    int pos = holder.getPosition();
                    //儘量在回撥時處理邏輯,此處不進行處理,增加Adapter複用的可能
                    mOnItemClickLitener.onItemClick(holder.itemView, pos);
                }
            });

            holder.itemView.setOnLongClickListener(new OnLongClickListener()
            {
                @Override
                public boolean onLongClick(View v)
                {
                    int pos = holder.getPosition();
                    //儘量在回撥時處理邏輯,此處不進行處理,增加Adapter複用的可能
                    mOnItemClickLitener.onItemLongClick(holder.itemView, pos);
                    return false;
                }
            });
        }
    }

    @Override
    public int getItemCount()
    {
        return mDatas.size();
    }
    //需要自己定義,新增資料後需要呼叫notifyItemInserted(position),效果類似於ListView的notifyDataSetChanged()
    //但是這裡會呼叫預設或者自定義的增加item動畫
    public void addData(int position)
    {
        mDatas.add(position, "Insert One");
        notifyItemInserted(position);
    }

    //需要自己定義,新增資料後需要呼叫notifyItemRemoved(position),效果類似於ListView的notifyDataSetChanged()
    //但是這裡會呼叫預設或者自定義的刪除item動畫
    public void removeData(int position)
    {
        mDatas.remove(position);
        notifyItemRemoved(position);
    }

    //檢視快取
    class MyViewHolder extends ViewHolder
    {

        TextView tv;

        //在構造方法中獲取控制元件
        public MyViewHolder(View view)
        {
            super(view);
            tv = (TextView) view.findViewById(R.id.id_num);

        }
    }
}

4.佈局

activity_main.xml

<RelativeLayout 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="${relativePackage}.${activityClass}" >

    <android.support.v7.widget.RecyclerView
        android:id="@+id/id_recyclerview"
        android:divider="#ffff0000"
        android:dividerHeight="10dp"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</RelativeLayout>

item_char.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@drawable/item_bg"
    android:layout_margin="2dp"
    android:gravity="center"
    >

    <TextView
        android:id="@+id/id_num"
        android:layout_width="100dp"
        android:layout_gravity="center"
        android:layout_height="100dp"
        android:gravity="center" />

</RelativeLayout>

這裡寫圖片描述

5.瀑布流

在Adpater裡面修改一下每個item的寬高,RecyclerView的LayoutManager再換成

mLlayoutmanager = new StaggeredGridLayoutManager(3,StaggeredGridLayoutManager.VERTICAL);
mGlayoutmanager = new StaggeredGridLayoutManager(5,StaggeredGridLayoutManager.HORIZONTAL);

就能看到瀑布流的效果啦。
這裡寫圖片描述

6.總結與demo下載

這其實是一個十分簡單的demo,你可以在此基礎上去修改RecyclerView的分隔線以及載入刪除動畫。
RecyclerView的強大之處就是解耦,以往常常將所有的程式碼,分隔線、動畫、邏輯處理、檢視快取等都放在Adapter中而導致Adapter根本無法複用。相信在大型專案中RecyclerView會有很好的表現以及程式設計體驗。
demo下載

注意:本demo有一處疏漏,需要在Manifest.xml中對對應需要 橫豎屏切換 的Activity新增一條屬性:

android:configChanges="orientation"