2016年11月26日 星期六

Android RecyclerView Introduce & Implement 介紹&實作

大部分的開發者對於ListView應該不陌生吧,但是今天要來介紹的是RecyclerView,Android是一個不斷進化的平台,Android 5.0的v7版本支持包中引入了新的RecyclerView控制項,正如官方文檔所言,RecyclerView是ListView的豪華增強版。它主要包含以下幾處新的特性,如ViewHolder,ItemDecorator,LayoutManager,SmothScroller以及增加或刪除item時item動畫等。官方推薦我們採用RecyclerView來取代ListView。

優點:



1) ViewHolder Pattern

在很多的ListView 範例中,常常會看到ViewHolder的使用,因為在ListView中有時候會大量的加載View,這樣會造成記憶體浪費,影響性能,這時候就可以使用ViewHolder 靜態類別方法的設計方法,避免造成過多的記憶體浪費,那在RecylerView 中強制要使用ViewHolder,雖然可能會變得比較複雜一點,但是我們在ListView中所遇到的一些問題都被更有效率的解決!
可以參考這篇ViewHolder用法
2) LayoutManager

ListView只提供垂直的上下滑動,不能水平滑動等,現在使用RecylerView提供了3種
i.   LinearLayoutManager         支援水平或垂直的清單(lists)
ii.  StaggeredLayoutManager  支援拼貼樣式像是交錯的清單
iii. GridLayoutManager            支援呈現網格像是圖集的清單
最好的一件事是我們可以動態地做我們想要呈現的layout
3) Item Animator
LisrView 缺少好的動畫,但是RecyclerView 帶來全新的動畫方式,藉由RecyclerView.ItemAnimator 類別,動畫的呈現變得更簡單於直觀,可以在添加刪除或移動項目時做動畫

4) Item Decoration
在ListView中,動態地裝飾項目像是增加界線或是分隔線都是不容易的,但是在RecyclerView的 RecyclerView.ItemDecorator 類別給予開發者很大的控制彈性,但卻也會造成更多的時間浪費與複雜
5) OnItemTouchListener
在ListView中點擊項目的動作是很簡單的,多虧有了 AdapterView.OnItemClickListenerinterface,
而RecyclerView 藉由RecyclerView.OnItemTouchListener給予開發者更強的控制,卻也變得比較複雜

介紹完我就在下面簡單實作RecyclerView

1:Gradle配置 build.gradle

compile 'com.android.support:recyclerview-v7:23.0.0'

2:建立Main Layout activity_recyclerview.xml



<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    
  xmlns:app="http://schemas.android.com/apk/res-auto"    
  xmlns:tools="http://schemas.android.com/tools"    
  android:id="@+id/activity_main"    
  android:layout_width="match_parent"    
  android:layout_height="match_parent"    
  tools:context="com.example.yi_an.myrecyleview.MainActivity">
    <android.support.v7.widget.RecyclerView        
      android:id="@+id/myrecycle"        
      android:layout_width="match_parent"        
      android:layout_height="match_parent"
     />

</LinearLayout>
由於測試需要多種item Layout的加載,需要建立2個item layout
3:建立Item 1 Layout
<?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">
    <TextView        
       android:text="TextView"        
       android:layout_width="match_parent"        
       android:layout_height="wrap_content"        
       android:textSize="20sp"        
       android:id="@+id/textView" />
</LinearLayout>

4:建立Item 2 Layout

<?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">
    <com.example.yi_an.myrecyleview.MySpinner        
        android:id="@+id/button"        
        android:layout_height="wrap_content"        
        android:layout_width="wrap_content"        
        android:textSize="20sp"        
     />
</LinearLayout>



5:建立Activity跟RecyclerView Adapter
RecyclerView.Adapter 負責提供 child-view 給 RecyclerView 使用,
同時也負責把資料跟 child-view 綁在一起。實際上並不是直接對上 View,
而是對上 ViewHolder。從設計上來看就是強迫使用 ViewHolder pattern 來增加效率。
此抽象類別需要實作這四個 method
  • getItemCount() - Adapter才知道如何儲存資料及回傳有多少筆資料
  • onCreateViewHolder(ViewGroup parent, int viewtype) - 現有的 ViewHolder不夠用,要求Adapter產生一個新的viewholder,也可以根據viewtype產生你要的viewholder(對應第四個function)
  • onBindViewHolder(ViewHolder viewholder, int position) - 重用之前產生的 ViewHolder,把特定位置的資料連結上去準備顯示
  • getItemViewType(int) - 根據位置回傳你要View的樣式
 public class MainActivity extends AppCompatActivity{
    private RecyclerView mRecyclerView;
    private RecyclerView.LayoutManager mLayoutManager;
    private MyAdapter mAdapter;
    private String [] myDataset;
    private List<MyItem> myItemList;
    private String [] selectdata;
    @Override    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        myDataset  = new String[]{"test","test1","test3","4","5","6","7","8"};
        myItemList = new ArrayList<>();
        MyItem myItem1 = new MyItem("1","select","1/2/3/4/5/6");
        MyItem myItem2 = new MyItem("2","text",null);
       
        myItemList.add(myItem1);
        myItemList.add(myItem2);
       
        selectdata = new String[myItemList.size()];
        mRecyclerView = (RecyclerView)findViewById(R.id.myrecycle);
        // use this setting to improve performance if you know that changes        // in content do not change the layout size of the RecyclerView        mRecyclerView.setHasFixedSize(true);
        mLayoutManager = new LinearLayoutManager(this);
        mRecyclerView.setLayoutManager(mLayoutManager);

        // specify an adapter (see also next example)        mAdapter = new MyAdapter(this,myItemList);
        mRecyclerView.setAdapter(mAdapter);
    }
}  
private class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{
        private  String[] mdataset;
        private  List<MyItem> list;
        private  LayoutInflater  mLayoutInflater;

        public class ViewHolder extends RecyclerView.ViewHolder {
            public MySpinner mySpinner;
            public ViewHolder(View v) {
                super(v);
                mySpinner = (MySpinner)v.findViewById(R.id.button);
            }
        }

        public class ViewHolder1 extends RecyclerView.ViewHolder {
            public TextView mTextView;
            public ViewHolder1(View v) {
                super(v);
                mTextView = (TextView) v.findViewById(R.id.textView);

            }
        }

        public MyAdapter(Context context, List<MyItem> myItem ){           
            list = myItem;
            mLayoutInflater = LayoutInflater.from(context);
        }


        @Override        
        public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            Log.d("recycle","onCreateViewHolder");
            if(viewType==0) {
                View v = mLayoutInflater.inflate(R.layout.my_spinner, parent, false);
                return new ViewHolder(v);
            }else {
                View v = mLayoutInflater.inflate(R.layout.my_text_view, parent, false);
                return new ViewHolder1(v);
            }
        }

        @Override        
        public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
            Log.d("recycle","onBindViewHolder");
                Log.d("position",position+"");
                if(holder instanceof ViewHolder){
                    ((ViewHolder) holder).mySpinner.initContent(MainActivity.this,list.get(position).value.split("/"),position);
                    if(selectdata[position]!=null){
                        ((ViewHolder) holder).mySpinner.setText(selectdata[position],null);
                    }
                    for(int x = 0; x<selectdata.length;x++){
                        if(selectdata[x]!=null)
                            Log.d("selectdata",selectdata[x]);
                    }
                }else if(holder instanceof ViewHolder1){
                    ((ViewHolder1) holder).mTextView.setText(list.get(position).value);
                }
        }

        @Override        
        public int getItemViewType(int position) {
            Log.d("recycle","getItemViewType");
            if("select".equals(list.get(position).type)){
                return 0;
            }else{
                return 1;
            }
        }

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


    }
附上gitgub
https://github.com/andy086912597/MyRecyclerView/tree/master
歡迎大家來討論



(References)
http://stackoverflow.com/questions/28525112/android-recyclerview-vs-listview-with-viewholder
https://read01.com/kE4z2.html


2016年11月1日 星期二

MPChart 折線圖 (多個折線)

很多人想要在App上加入一些數據分析圖表,光想要怎麼做就很頭痛八,感謝神人推出MPChart套件可以使用,就讓我們一起來看吧!

先來介紹MPChart 是什麼?

官網:

MPAndroidChart is a powerful & easy to use chart library for Android. It runs on API level 8 and upwards.
As an additional feature, this library allows cross-platform development between Android and iOS as an iOS version of this library is also available
簡單來說它就是一個"圖表"Libraryandroid ios使用。
Wow,那不就很強大,對於想要在視覺化呈現你的數字、資料,我想這個真的必會!

優點:

八種不同的圖表類型

  1.  縮放XY(藉由手勢觸碰、滑動)
  2.  組合圖表
  3. 可訂制XY軸的值
  4.  圖表儲存
  5.  動畫產生值
  6. 可訂製的圖表(字體、顏色、背景、手勢、圖點)
  7.  會隨著你的值縮放圖表

圖表類型:
  1.     長條圖
  2.     折線圖
  3.     圓餅圖
  4.    散點圖
  5.    泡泡圖
  6.    直方圖
  7.    雷達圖
  8.    組合圖

其實官方的網站都有講到這裡附上,那接下來就來實作我們的折線圖

一、首先,在你的專案加入此Lib
        build.gradle裡加入
    dependencies {compile 'com.github.PhilJay:MPAndroidChart:v3.0.0-beta1'}

二、開始在你的layout加入CustomerView,這裡我們要做折線圖
    <com.github.mikephil.charting.charts.LineChart
    android:id="@+id/chart"   
android:layout_width="match_parent"    android:layout_height="match_parent"   
android:layout_margin="10dp" />

三、Activiy定義、設置圖表style、設置圖表功能、設置圖表值並實作出來

定義:LineChart mchart = (LineChart)findViewById(R.id.chart);

設置圖表style
        mChart.setDescription(mDataSource.getDescription());//chart上的右下角加描述
        mChart.setDescriptionTextSize(30);
        mChart.setUnit("%"); //設置Y轴上的单位
        mChart.setAlpha(0.8f);//設置透明度
        mChart.setBorderColor(Color.rgb(213, 216, 214));//設置網格底下的那條線的颜色
        mChart.setBackgroundColor(Color.rgb(255, 255, 255));//設置背景顏色

設置圖表功能:
        mChart.setHighlightEnabled(true);//設置焦點顯示        
         mChart.setTouchEnabled(true);//設置是否可以觸摸,如為false,則不能拖動,縮放等
        mChart.setDragEnabled(true);//設置是否可以拖拽,缩放
        mChart.setScaleEnabled(true);//設置是否在圖點上顯示值
        mChart.setPinchZoom(true); //設置是否能變大變小

設置圖表值:
     我想這個是大家最想知道的
     首先我利用ArrayList去存我的XY(橫、縱軸)

     List<Entry> entries = new ArrayList<>();
{
    x = {1,2,3};
    y = {3,2,1}
    for (int i = 0; i < x.size(); ++i) {
        Entry e = new Entry(x[i], y[i]);
        entries.add(e);
    }
}
LineDataSet dataSet = new LineDataSet(entries, "");
//折線的顏色
dataSet.setColor(Color.GRAY);
dataSet.setLineWidth(3.0f);
//線下方填滿
dataSet.setDrawFilled(true);
dataSet.setFillColor(Color.parseColor("#FFFD464E"));
//圈圈外圍大小跟顏色
dataSet.setDrawCircles(true);
dataSet.setCircleColor(Color.WHITE);
dataSet.setCircleRadius(8.0f);
//圈圈裡面洞大小跟顏色
dataSet.setDrawCircleHole(true);
dataSet.setCircleColorHole(Color.GRAY);
dataSet.setCircleHoleRadius(4.0f);
//要不要在點上面顯示數值
dataSet.setDrawValues(false);
     注意的是這裡的EntryMPChart libclass Entry(float x, float y)
     ,所以要記得把XY轉型成 Float

最後就把數值實作在MPChart裡,就大功告成
  mchart.setData(lineData);

最後教大家怎麼在一張圖放置多個折線

還記得我一開始教大家怎麼存取我們的XY值嗎?!

    List<Entry> entries = new ArrayList<>();
{
    x = {1,2,3};
    y = {3,2,1}
    for (int i = 0; i < x.size(); ++i) {
        Entry e = new Entry(x[i], y[i]);
        entries.add(e);
    }
LineDataSet dataSet = new LineDataSet(entries, "");

那我們現在要創建一個LineDataSetArrayList去存取我們多個折線的值

ArrayList<ILineDataSet> dataSets = new ArrayList<ILineDataSet>();
dataSets.add(dataSet);
dataSets.add(dataSet2);
dataSets.add(dataSet3);
....以此類推
這樣就可以放多個折線

今天就到一段落,這裡不放Code,網路上很多,如果有問題也可以互相切搓