show code block

2017年7月24日 星期一

Android元件(小專題一) — startActivityForResult、onActivityResult activity之間的資料傳遞

前言:

有感而發整理一下之前的資料。
startActivityForResult、onActivityResult之間的關西


先來進入八奇思考領域的第一步吧

情境:A activity 要到B activity (以下簡稱A和B),並且B 結束之後會回到 A

 1、startActivityForResult用這個會將畫面帶到B,結束之後B 將會返回A,並帶值過來
2、onActivityResult這個是在應付B回到A之後,要如何接應你的數據在這之間Bundle就像郵差。

A就是你的老闆,你不想回信都還不行。
B就是職員,一定要回老闆信。
而中間的Bundle就是傳遞員
startActivityForResult 就是郵筒,
而你要把信寄到onActivityResult就像秘書,秘書會處理一切收到的回信。

但假如A老闆有很多位員工C、D、E、F呢?
這就是傳令識別碼的功用了,你可以設定成任何你想設的模樣,只要A老闆 和其他員工之間特別的號碼,也就像員工編號一樣。


解釋完畢。END。好拉開玩笑的,讓我們來看看這是怎麼運作的。




程式碼:

這邊做一個顏色和文字間的傳遞作為示範

activity_main.xml (慣老闆)
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.hello.kaiser.colorpickertest.MainActivity">

    <TextView
        android:id="@+id/tv_color"
        android:layout_width="300dp"
        android:layout_height="300dp"
        android:layout_centerHorizontal="true"
        android:gravity="center"
        android:text="顏色未色定" />

    <Button
        android:onClick="selectColor"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/tv_color"
        android:layout_centerHorizontal="true"
        android:text="選擇顏色" />

</RelativeLayout>
 

activity_color_picker.xml(員工A)
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.hello.kaiser.colorpickertest.ColorPicker">


    <RadioGroup
        android:id="@+id/icon_group"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:gravity="center"
        android:orientation="vertical">

        <RadioButton
            android:id="@+id/radio_holo_red_light"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:buttonTint="@android:color/holo_red_light"
            android:checked="true"
            android:onClick="clickColor"
            android:text="holo_red_light"
            android:textColor="@android:color/holo_red_light" />

        <RadioButton
            android:id="@+id/holo_orange_dark"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:buttonTint="@android:color/holo_orange_dark"
            android:onClick="clickColor"
            android:text="holo_red_light"
            android:textColor="@android:color/holo_orange_dark" />

        <RadioButton
            android:id="@+id/holo_green_light"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:buttonTint="@android:color/holo_green_light"
            android:onClick="clickColor"
            android:text="holo_red_light"
            android:textColor="@android:color/holo_green_light" />

        <RadioButton
            android:id="@+id/holo_blue_dark"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:buttonTint="@android:color/holo_blue_dark"
            android:onClick="clickColor"
            android:text="holo_red_light"
            android:textColor="@android:color/holo_blue_dark" />
    </RadioGroup>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/icon_group"
        android:gravity="center"
        android:orientation="horizontal">

        <Button
            android:onClick="canncel"
            android:id="@+id/cancel_action"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="canncel" />

        <Button
            android:onClick="ok"
            android:id="@+id/ok"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="ok" />
    </LinearLayout>
</RelativeLayout>



BundleKey.java (傳遞員)
package com.hello.kaiser.colorpickertest;

/**
 * Created by kaiser on 2017/7/24.
 */

public class BundleKey {
    public static final String BUNDLE_KEY_COLOR_INT = "com.daniel.android.colorint";
    public static final String BUNDLE_KEY_COLOR_NAME = "com.daniel.android.colorname";
}

 
MainActivity.java(老闆)
package com.hello.kaiser.colorpickertest;

import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

    //傳令識別碼(這就像員工編號,只要信上的員工編號不是指你,你都不能開信。當然也不會想開xd)
    private static final int SELECT_COLOR_REQUEST = 0;

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

    public void selectColor(View view) {
        //信箱寄信囉,員工編號顯示為 SELECT_COLOR_REQUEST
        startActivityForResult(new Intent(this, ColorPicker.class), SELECT_COLOR_REQUEST);
    }

    //老闆秘書,處理老闆一切收到的信件
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == SELECT_COLOR_REQUEST) {//員工編號等於SELECT_COLOR_REQUEST,秘書開始處理事情
            if (resultCode == RESULT_OK) {//收到的結果是result_ok的話
                TextView textview = (TextView) findViewById(R.id.tv_color);

                Bundle bundle = data.getExtras();
                int mColorInt = bundle.getInt(BundleKey.BUNDLE_KEY_COLOR_INT);
                CharSequence mColorNmae = bundle.getCharSequence(BundleKey.BUNDLE_KEY_COLOR_NAME);

                textview.setText(mColorNmae);
                textview.setBackgroundColor(mColorInt);
            }
            if (resultCode==RESULT_CANCELED){//如果收到的結果是result_canceled的話
                Toast.makeText(this, "取消選擇", Toast.LENGTH_SHORT).show();
            }
        }
    }
}
 


ColorPicker.java(員工A)
package com.hello.kaiser.colorpickertest;

import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.RadioButton;

public class ColorPicker extends AppCompatActivity {

    private int mColorInt;
    private CharSequence mColorName;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_color_picker);
        initColorData();
    }

    private void initColorData() {
        RadioButton radio = (RadioButton) findViewById(R.id.radio_holo_red_light);
        mColorName = radio.getText();
        mColorInt = radio.getCurrentTextColor();
    }

    public void clickColor(View view) {
        RadioButton radio = (RadioButton) view;
        mColorInt = radio.getCurrentTextColor();
        mColorName = radio.getText();
    }


    public void ok(View view) {//員工選擇好要回信囉
        Intent intent = new Intent();
        intent.putExtra(BundleKey.BUNDLE_KEY_COLOR_NAME, mColorName);
        intent.putExtra(BundleKey.BUNDLE_KEY_COLOR_INT, mColorInt);
        setResult(RESULT_OK, intent);
        finish();
    }

   public void canncel(View view) {//取消
        setResult(RESULT_CANCELED);
        finish();
    }
}
 







複製貼上就出來了,不提供demo囉,有問題可以提出討論。

2017年7月23日 星期日

Android元件(SearchView) — SearchView使用


前言:

可以代替EditText的東西。
如果要做收尋一般我都會想到EditText。
但android有一個官方使用的SearchView
也可以考慮使用

程式碼:

activity_main.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:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.hello.kaiser.searchviewtest.MainActivity">

    <android.support.v7.widget.SearchView
        android:id="@+id/search_view"
        style="@style/Theme.SearchView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Hello World!" />

</LinearLayout>

 

看到上方佈局文件內的style嗎?
我們要在values內的styles寫入我們需要的東西。
styles.xml
<style name="Theme.SearchView" parent="Base.Widget.AppCompat.SearchView">
        <item name="searchIcon">@drawable/search_icon</item>
        <item name="closeIcon">@drawable/icon_search_bar_dismiss</item>
        ·<item name="queryHint">你好我是自訂的底文字</item>
        <!--<item name="queryBackground">@drawable/background</item>-->
    </style>
 

上面drawable內的東西就自己去google下載自己喜歡的圖面囉。這裡就不提供了。

可以試試看效果了























大概就是上述的效果。
如果不需要icon點進去,想要直接顯示SearchView的話呢?
  //原本點擊SearchIcon才會出現搜尋的那個icon,改成在整條searchView前方顯示
        mSearchView.setIconifiedByDefault(false);
 

此時就會變成這樣。
原本的searchIcon會移到前面去。
後面的closeIcon在你已經打了文字後才會出現。而且closeIcon的效果變成刪除所有文字內容。




































如此我們客製化的內容已經完成了。
但如果我們要讓前面的searchIcon消失呢?
因為styles內的設定不可以是null
所以我們就可以使用程式碼。
  //讓searchIcon消失or自訂
        ImageView searchIcon = (ImageView) mSearchView.findViewById(android.support.v7.appcompat.R.id.search_mag_icon);
        searchIcon.setImageDrawable(null);
 
順利讓前方的null消失了!


除了style的方式外,也可以拿程式碼的方式更換自己想要的圖片。
我提供兩個示意,方法可以自己依樣畫葫蘆。上方用style可以完成的事情,用程式碼都可以完成。
  //自訂closeIcon
        ImageView closeIcon = (ImageView) mSearchView.findViewById(android.support.v7.appcompat.R.id.search_close_btn);
        closeIcon.setImageResource(R.drawable.search_dismiss);
        mSearchView.setQueryHint("搜尋...");

        //自訂文字size
        TextView textview = (TextView) mSearchView.findViewById(android.support.v7.appcompat.R.id.search_src_text);
        textview.setTextSize(TypedValue.COMPLEX_UNIT_SP, getResources().getDimension(R.dimen.default_font_size));

 
還有一個比較特別的submit方法。
在SearchView旁邊會出現一個箭頭,按下去就是送出搜尋結果的意思。
 //設置true打了文字後旁會邊出現箭頭,代表submit
        mSearchView.setSubmitButtonEnabled(true);
 



客製化的畫面完成囉。

簡易使用:

這時候就要參照官方文檔了。
https://developer.android.com/reference/android/widget/SearchView.html

mSearchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
            @Override
            public boolean onQueryTextSubmit(String query) {
                Toast.makeText(MainActivity.this, "搜尋結果為:" + query, Toast.LENGTH_SHORT).show();
                return false;
            }

            @Override
            public boolean onQueryTextChange(String newText) {
                Log.d("當文字改變時", "改變的文字 :" + newText);
                return false;
            }
        });
 

如上述onQueryTTextSubmit是當你按下enter時觸發。
onQueryTextChange是當你在SearchView上打的每一個文字都會觸發的效果,詳細可以見你的Log

完整MainActivity.java程式碼:
public class MainActivity extends AppCompatActivity {

    private SearchView mSearchView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mSearchView = (SearchView) findViewById(R.id.search_view);

        //讓searchIcon消失or自訂
        ImageView searchIcon = (ImageView) mSearchView.findViewById(android.support.v7.appcompat.R.id.search_mag_icon);
        searchIcon.setImageDrawable(null);

        //自訂closeIcon
//        ImageView closeIcon = (ImageView) mSearchView.findViewById(android.support.v7.appcompat.R.id.search_close_btn);
//        closeIcon.setImageResource(R.drawable.search_dismiss);
//        mSearchView.setQueryHint("搜尋...");

        //自訂文字size
//        TextView textview = (TextView) mSearchView.findViewById(android.support.v7.appcompat.R.id.search_src_text);
//        textview.setTextSize(TypedValue.COMPLEX_UNIT_SP, getResources().getDimension(R.dimen.default_font_size));

        //原本點擊SearchIcon才會出現搜尋的那個icon,改成在整條searchView前方顯示
        mSearchView.setIconifiedByDefault(false);
        //設置true打了文字後旁會邊出現箭頭,代表submit
        mSearchView.setSubmitButtonEnabled(true);

        mSearchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
            @Override
            public boolean onQueryTextSubmit(String query) {
                Toast.makeText(MainActivity.this, "搜尋結果為:" + query, Toast.LENGTH_SHORT).show();
                return false;
            }

            @Override
            public boolean onQueryTextChange(String newText) {
                Log.d("當文字改變時", "改變的文字 :" + newText);
                return false;
            }
        });
    }
}

 
demo:
https://drive.google.com/open?id=0Byk75IYx-dKXZzJqWWo5b0RSSkU



2017年7月20日 星期四

Android方法(RecyclerView) — RecyclerView外接到activity點擊事件

前言:

最近比較有空,把之前做的一些東西整理一下。
其中還有一個RecyclerView的外接點擊事件,就是模擬ListView的setOnItemClickListener的方法。
RecyclerView的點擊事件通常都直接寫在onBindViewHolder內,直接對holder內的元件寫onClick。
但如果要拉到activity內寫就要額外製作了。
我把上一部的DEMO拿來做示範。
DEMO下載: https://drive.google.com/open?id=0Byk75IYx-dKXNXEzdzlzUEwzc3M

今日實作
 


 簡單步驟:

一、在RecyclerView內holder這個方法必須implements View.OnClickListener 寫onClick方法
二、寫interface方法,在方法內寫onClickHello 抓取view和position
三、在RecyclerView內宣告剛剛寫的interface這方法
四、寫setListener讓activity使用回調
五、在holder內註冊你元件的監聽事件
完成!
這樣就可以在activity內調用adapter的點擊事件了

一、在RecyclerViewAdapter內holder這個方法必須implements View.OnClickListener 寫onClick方法
 //1、implements View.OnClickListener 寫onClick方法
    class dataHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
        private ImageView imageView;
        private TextView textView;

        public dataHolder(View itemView) {
            super(itemView);
            imageView = (ImageView) itemView.findViewById(R.id.image_view);
            textView = (TextView) itemView.findViewById(R.id.text_view);
            //5、註冊監聽事件
            itemView.setOnClickListener(this);
            textView.setOnClickListener(this);
        }

        @Override
        public void onClick(View view) {
            if (clickListener != null)
                clickListener.onClickHello(view, getAdapterPosition());
        }
    }

 
二、寫interface方法,在方法內寫onClickHello 抓取view和position

寫在RecyclerViewAdapter內。
 //2、寫interface 方法 在方法內寫onClcikHello 抓取view和position
    public interface onItemClickListener {
        void onClickHello(View view, int position);
    }
 

三、在RecyclerView內宣告剛剛寫的interface這方法
    private onItemClickListener clickListener;//3、在元件這註冊
 

四、寫setListener讓activity使用回調
 //4、寫setListener讓activity使用回調
    public void setOnItemClickListener(onItemClickListener listener) {
        this.clickListener = listener;
    }
 


五、在holder內註冊你元件的監聽事件
 //1、implements View.OnClickListener 寫onClick方法
    class dataHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
        private ImageView imageView;
        private TextView textView;

        public dataHolder(View itemView) {
            super(itemView);
            imageView = (ImageView) itemView.findViewById(R.id.image_view);
            textView = (TextView) itemView.findViewById(R.id.text_view);
            //5、註冊監聽事件
            itemView.setOnClickListener(this);
            textView.setOnClickListener(this);
        }

        @Override
        public void onClick(View view) {
            if (clickListener != null)
                clickListener.onClickHello(view, getAdapterPosition());
        }
    }
 

Adapter完成後,讓我們把畫面轉到MainActivity.java內,開始使用它吧!
 dataAdapter.setOnItemClickListener(new DataAdapter.onItemClickListener() {
            @Override
            public void onClickHello(View view, final int position) {
                Toast.makeText(MainActivity.this, "點擊事件position = " + position, Toast.LENGTH_SHORT).show();
            }
        });
 




完整Adapter程式碼:

 

class DataAdapter extends RecyclerView.Adapter<DataAdapter.dataHolder> implements Filterable {

    private final static String TAG = DataAdapter.class.getSimpleName();

    //元件
    private Context context;
    private ArrayList<Data> datalists;//是會變動的陣列,用來顯示正個recyclerView的資料
    private ArrayList<Data> filterDatas;//固定陣列,用來和filter比對用的。
    private MyFliter myFliter;//讓mainActivity能使用adapter呼叫篩選功能
    private onItemClickListener clickListener;//3、在元件這註冊

    //2、寫interface 方法 在方法內寫onClcikHello 抓取view和position
    public interface onItemClickListener {
        void onClickHello(View view, int position);
    }

    //4、寫setListener讓activity使用回調
    public void setOnItemClickListener(onItemClickListener listener) {
        this.clickListener = listener;
    }

    //建構子
    public DataAdapter(Context context) {
        this.context = context;
    }

    //implements Filterable的必定覆寫的方法,讓我們在activity內能呼叫他
    @Override
    public Filter getFilter() {
        if (myFliter == null) {
            myFliter = new MyFliter();
        }
        return myFliter;
    }

    //1、implements View.OnClickListener 寫onClick方法
    class dataHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
        private ImageView imageView;
        private TextView textView;

        public dataHolder(View itemView) {
            super(itemView);
            imageView = (ImageView) itemView.findViewById(R.id.image_view);
            textView = (TextView) itemView.findViewById(R.id.text_view);
            //5、註冊監聽事件
            itemView.setOnClickListener(this);
            textView.setOnClickListener(this);
        }

        @Override
        public void onClick(View view) {
            if (clickListener != null)
                clickListener.onClickHello(view, getAdapterPosition());
        }
    }

    //讓我們在activity內能讀取或更新adapter內data的資料
    public void setData(ArrayList<Data> datalists) {
        this.datalists = datalists;
        filterDatas = datalists;
        notifyDataSetChanged();
    }

    @Override
    public DataAdapter.dataHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false);
        dataHolder dh = new dataHolder(view);
        return dh;
    }

    @Override
    public void onBindViewHolder(DataAdapter.dataHolder holder, int position) {
        Glide.with(context)
                .load(datalists.get(position).getImageUrl())
                .placeholder(R.drawable.default_image)
                .into(holder.imageView);

        if (!TextUtils.isEmpty(datalists.get(position).getTitle()))
            holder.textView.setText(datalists.get(position).getTitle());
        else
            holder.textView.setText("暫無資料");
    }

    @Override
    public int getItemCount() {
        return datalists == null ? 0 : datalists.size();
    }

    //實作自己的篩選Filter
    class MyFliter extends Filter {
        @Override
        protected FilterResults performFiltering(CharSequence filterContent) {
            ArrayList<Data> filterData = new ArrayList<>();
            //先判斷filterContent是不是null才進入
            if (filterContent != null && filterContent.toString().trim().length() > 0) {
                Log.d(TAG, "確認是否為空值。 filterData.size = " + filterData.size());
                //這回圈是在判斷你輸入的文字(filterContent)是否有在filterDatas陣列內有相關的文字,逐條搜尋。
                for (int i = 0; i < filterDatas.size(); i++) {
                    String content = filterDatas.get(i).getTitle();
                    Log.d(TAG, "確認是否進入for迴圈 content = " + content);
                    if (content.contains(String.valueOf(filterContent))) {
                        Log.d(TAG, "確認輸入文字是否相同。");
                        Data data = new Data();
                        data.setImageUrl(filterDatas.get(i).getImageUrl());
                        data.setTitle(filterDatas.get(i).getTitle());
                        filterData.add(data);
                    }
                }
            } else {
                filterData = filterDatas;
                Log.d(TAG, "確認什麼都沒打 filterDatas = datalists = " + filterData.size());

            }
            FilterResults filterResults = new FilterResults();
            filterResults.count = filterData.size();
            filterResults.values = filterData;
            Log.d(TAG, "final size = " + filterResults.count);
            return filterResults;
        }

        @Override
        protected void publishResults(CharSequence charSequence, FilterResults filterResults) {
            datalists = (ArrayList<Data>) filterResults.values;
            if (filterResults.count > 0) {
                notifyDataSetChanged();
            } else {
                Data data = new Data();
                data.setTitle("沒有結果");
                datalists.add(data);
                notifyDataSetChanged();
            }
        }
    }
}


DEMO:

https://drive.google.com/open?id=0Byk75IYx-dKXQWo5ZF9IeDdlZFE

2017年7月17日 星期一

Android元件(SearchView、Filter) — SearchView應用、在RecyclerView內創建Filter

 前言:

 最近做到這個功能,在這裡做個紀錄。
以前我使用SearchBar的功能是我給後台值,後台直接給我收尋結果。
但如果使用到的收尋結果必須要前端(App端)實作呢?
這裡就教你如何前端做收尋應用。



重點程式碼:

重點都是圍繞在Filter
可以看一下官方文檔, 讓我們把畫面帶到Protected methods
在待會我們實作RecyclerView的時候繼承 Filterable 會用到getFilter() 方法,好讓我們在activity調用adapter時使用篩選。
還會再Adapter內實作一個自己的Filter,這時候會override複寫下方兩種方法。
performFiltering 執行篩選
publishResults 篩選結果

SearchView的部分,必須implements SearchView.OnQueryTextListener

Public methods

abstract booleanonQueryTextChange(String newText)
Called when the query text is changed by the user.
abstract booleanonQueryTextSubmit(String query)
Called when the user submits the query.

onQueryTextChange 當使用者改變searchview的字體時,call此方法。
onQueryTextSubmit 當使用者按下送出時,call此方法。



實作:

這裡提供 SearchView + ListView 的實作方法。
[AndroidStudio] 使用 SearchView 過濾列表資料 —
http://disp.cc/b/11-9ded

裡面的內容清楚明瞭。
如果有問題可以在這裡問。
我實作了一個demo:https://drive.google.com/open?id=0Byk75IYx-dKXWTUya0hKaWtXX3M

但ListView近期已經被RecyclerView代替了。
在RecyclerView內的adapter並沒有filter的方法,因此我們要在Adapter內實作。

提供一個SearchView + RecyclerView的方法
作者:Lipt0n
連結:http://www.jianshu.com/p/5078c7fec29e
來源:簡書
著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。

此處就是在Adapter內實作Filterable並使用的很好範例。
等等我做的東西是實踐在這個上面的東西,就不提供DEMO了。


所有的收尋都拿String當例子,簡單明瞭!

但如果我要客製化呢?變成一個item的搜尋模式該怎麼辦呢?
這邊包含可以搜尋文字。
如果每個item的文字都不一樣,是可以分開收尋的。
只要你的文字內有關鍵字,都會跑在下面的結果內。
如圖:




完整程式碼:

先來看看 今日我們會動到的地方:



 因為會有網路下載圖片的功能,所以先在manifest加入Gilde。
如何加入這篇有提到
http://nikeru8.blogspot.tw/2017/03/third-party-frescopicasso.html
就不再贅述。

然後你還要一張default image,供給如果Gilde還沒下載完成時的預設圖片。
http://lmgtfy.com/?q=default_image 

這邊必須強調一下預設圖片和文字的重要性,如果RecyclerView讀取不到你的圖片或是文字,因為他內在的回收機制,他會去偷取其他item的圖片作為預設圖片。你也知道後果了。文不對題,或是錯位的問題就會絡繹不絕。


activity_main.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:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.hello.kaiser.searchbar.MainActivity">

    <android.support.v7.widget.SearchView
        android:id="@+id/search_bar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

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

</LinearLayout>

 

先看activity的佈局文件。
先把xml畫好畫滿。
item.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="horizontal">

    <ImageView
        android:id="@+id/image_view"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:padding="5dp" />

    <TextView
        android:id="@+id/text_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:gravity="center"
        android:textSize="30dp" />
</LinearLayout>
 

再來先把工具寫出來,我們要塞物件的DATA
DATA.java
 
public class Data  {
    private String title;
    private String imageUrl;

//    public Data(String title, String imageUrl) {
//        this.title = title;
//        this.imageUrl = imageUrl;
//        Log.d("ItemData", "CheckPoint imageUrl=" + imageUrl);
//    }

    public String getTitle() {
        return title;
    }

    public String getImageUrl() {
        return imageUrl;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public void setImageUrl(String imageUrl) {
        this.imageUrl = imageUrl;
    }

}

這裡我們要塞圖片和文字。

把我們MainActivity.java 全部補上吧。
public class MainActivity extends AppCompatActivity implements SearchView.OnQueryTextListener {

    
    //元件
    private android.support.v7.widget.SearchView searchView;
    private RecyclerView recyclerView;
    private ArrayList<Data> datalists = new ArrayList<>();
    private String[] urlList = null;
    private String[] titleList = null;
    private DataAdapter dataAdapter;


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

    /**
     *  這方法在activity所有畫面都完成之後,才會被call。
     *  之前我做的專案,有很多Crash案例都是因為畫面還沒完成,點擊事件就先放行,點擊後導致Crash。
     */
    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        super.onWindowFocusChanged(hasFocus);
        initListener();
    }

    /**
     *  init畫面
     */
    private void initView() {
        searchView = (android.support.v7.widget.SearchView) findViewById(R.id.search_bar);
        recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
    }

    /**
     *  init設定
     */
    private void initSet() {
        //RecyclerView設定
        dataAdapter = new DataAdapter(this);
        LinearLayoutManager llm = new LinearLayoutManager(this);
        llm.setOrientation(LinearLayoutManager.VERTICAL);
        recyclerView.setLayoutManager(llm);
        searchView.setOnQueryTextListener(this);
        searchView.setIconifiedByDefault(false); //是否要點選搜尋圖示後再打開輸入框
        searchView.setFocusable(false);
        searchView.requestFocusFromTouch();      //要點選後才會開啟鍵盤輸入
        searchView.setSubmitButtonEnabled(false);//輸入框後是否要加上送出的按鈕
        searchView.setQueryHint("請輸入android搜尋"); //輸入框沒有值時要顯示的提示文字

        //假資料設定
        urlList = new String[]{
                "http://www.jrtstudio.com/sites/default/files/ico_android.png",
                "https://developer.android.com/_static/images/android/touchicon-180.png",
                "http://cdn.bgr.com/2012/11/android-icon.jpg?quality=98&amp;strip=all",
                "http://www.fastbooting.com/uploads/5/5/9/9/55994511/1474140_orig.png",
                "http://www.pendidikan-diy.go.id/dinas_v4/img/up_down/Android.png",
                "https://www.android.com/static/2016/img/one/a1_andy_1x.png",
                "http://zdnet2.cbsistatic.com/hub/i/2016/04/22/a1ced73f-6628-4930-96f4-d224e1d3a707/8a43709ccee674396403ee99472e38f3/android-security-1.jpg",
                "https://unwire.pro/wp-content/uploads/2017/05/android-kotlin.png",
                "http://www.jrtstudio.com/sites/default/files/ico_android.png",
                "https://developer.android.com/_static/images/android/touchicon-180.png",
                "http://cdn.bgr.com/2012/11/android-icon.jpg?quality=98&amp;strip=all",
                "http://www.fastbooting.com/uploads/5/5/9/9/55994511/1474140_orig.png",
                "http://www.pendidikan-diy.go.id/dinas_v4/img/up_down/Android.png",
                "https://www.android.com/static/2016/img/one/a1_andy_1x.png",
                "http://zdnet2.cbsistatic.com/hub/i/2016/04/22/a1ced73f-6628-4930-96f4-d224e1d3a707/8a43709ccee674396403ee99472e38f3/android-security-1.jpg",
                "https://unwire.pro/wp-content/uploads/2017/05/android-kotlin.png",
        };
        titleList = new String[]{
                "手機:iphone7",
                "手機殼:犀牛盾",
                "手機:htc",
                "電腦:戴爾",
                "手機:samsung",
                "電腦:hp惠普",
                "電腦:惠普",
                "電腦:蘋果電腦apple",
                "手機:sony",
                "手機:samsung s8",
                "手機:asus",
                "電腦:Auser宏碁電腦",
                "手機、電腦:微軟",
                "手機:oppo 蕩漾款",
                "手機:小米",
                "電腦:聯想",
        };

        //兩筆假資料size()一樣才進入迴圈
        if (titleList.length == urlList.length)
            for (int i = 0; i &lt; (urlList.length); i++) {
                Data data = new Data();//製作單筆資料
                data.setTitle(titleList[i]);
                data.setImageUrl(urlList[i]);
                datalists.add(data);//把單筆資料加入陣列
            }

        dataAdapter.setData(datalists);//把陣列塞入adapter
        recyclerView.setAdapter(dataAdapter);//把adapter塞入recyclerview
    }


    /**
     *  點擊事件
     */
    private void initListener() {

    }

    /**
     *  複寫implements SearchView.OnQueryTextListener 的方法
     */
    @Override
    public boolean onQueryTextSubmit(final String query) {
        return false;
    }

    @Override
    public boolean onQueryTextChange(final String newText) {
        //塞選 我們寫在adapter內
        dataAdapter.getFilter().filter(newText);
        return false;
    }
}

重頭戲Adapter
DataAdapter.java
class DataAdapter extends RecyclerView.Adapter<DataAdapter.dataHolder> implements Filterable {

    private final static String TAG = DataAdapter.class.getSimpleName();

    //元件
    private Context context;
    private ArrayList<data> datalists;//是會變動的陣列,用來顯示正個recyclerView的資料
    private ArrayList<data> filterDatas;//固定陣列,用來和filter比對用的。
    private MyFliter myFliter;//讓mainActivity能使用adapter呼叫篩選功能

    //建構子
    public DataAdapter(Context context) {
        this.context = context;
    }

    //implements Filterable的必定覆寫的方法,讓我們在activity內能呼叫他
    @Override
    public Filter getFilter() {
        if (myFliter == null) {
            myFliter = new MyFliter();
        }
        return myFliter;
    }

    class dataHolder extends RecyclerView.ViewHolder {
        private ImageView imageView;
        private TextView textView;

        public dataHolder(View itemView) {
            super(itemView);
            imageView = (ImageView) itemView.findViewById(R.id.image_view);
            textView = (TextView) itemView.findViewById(R.id.text_view);
        }
    }

    //讓我們在activity內能讀取或更新adapter內data的資料
    public void setData(ArrayList<data> datalists) {
        this.datalists = datalists;
        filterDatas = datalists;
        notifyDataSetChanged();
    }

    @Override
    public DataAdapter.dataHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false);
        dataHolder dh = new dataHolder(view);
        return dh;
    }

    @Override
    public void onBindViewHolder(DataAdapter.dataHolder holder, int position) {
        Glide.with(context)
                .load(datalists.get(position).getImageUrl())
                .placeholder(R.drawable.default_image)
                .into(holder.imageView);

        if (!TextUtils.isEmpty(datalists.get(position).getTitle()))
            holder.textView.setText(datalists.get(position).getTitle());
        else
            holder.textView.setText("暫無資料");
    }

    @Override
    public int getItemCount() {
        return datalists == null ? 0 : datalists.size();
    }

    //實作自己的篩選Filter
    class MyFliter extends Filter {
        @Override
        protected FilterResults performFiltering(CharSequence filterContent) {
            ArrayList<data> filterData = new ArrayList&lt;&gt;();
            //先判斷filterContent是不是null才進入
            if (filterContent != null &amp;&amp; filterContent.toString().trim().length() &gt; 0) {
                Log.d(TAG, "確認是否為空值。 filterData.size = " + filterData.size());
                //這回圈是在判斷你輸入的文字(filterContent)是否有在filterDatas陣列內有相關的文字,逐條搜尋。
                for (int i = 0; i &lt; filterDatas.size(); i++) {
                    String content = filterDatas.get(i).getTitle();
                    Log.d(TAG, "確認是否進入for迴圈 content = " + content);
                    if (content.contains(String.valueOf(filterContent))) {
                        Log.d(TAG, "確認輸入文字是否相同。");
                        Data data = new Data();
                        data.setImageUrl(filterDatas.get(i).getImageUrl());
                        data.setTitle(filterDatas.get(i).getTitle());
                        filterData.add(data);
                    }
                }
            } else {
                filterData = filterDatas;
                Log.d(TAG, "確認什麼都沒打 filterDatas = datalists = " + filterData.size());

            }
            FilterResults filterResults = new FilterResults();
            filterResults.count = filterData.size();
            filterResults.values = filterData;
            Log.d(TAG, "final size = " + filterResults.count);
            return filterResults;
        }

        @Override
        protected void publishResults(CharSequence charSequence, FilterResults filterResults) {
            datalists = (ArrayList<data>) filterResults.values;
            if (filterResults.count &gt; 0) {
                notifyDataSetChanged();
            } else {
                Data data = new Data();
                data.setTitle("沒有結果");
                datalists.add(data);
                notifyDataSetChanged();
            }
        }
    }
}
MyFliter 這方法內強破複寫兩種方法
performFiltering 執行篩選
publishResults 篩選結果
performFiltering
1、我先判斷在SearchView內的有無輸入任何文字(是否為null)
2、在寫一個for迴圈,把所有進入adatper陣列資料內的title String做比對,如果有相同的文字,就創建一個data物件,塞入你要篩選存放的ArrayList內。
3、 比對完之後把你篩選存放的ArrayList丟入FilterResults內。
publishFiltering
1、datalists是在adapter內會變動的陣列,用來顯示RecyclerView用的(filterDatas為固定陣列,用來和filter做資料比對)
2、 把剛剛在performFiltering抓取到的filterResults匯到datalists內。用以顯示recyclerView
3、假如陣列大於0,就notifyDataSetChanged刷新RecyclerView。


DEMO: https://drive.google.com/open?id=0Byk75IYx-dKXNXEzdzlzUEwzc3M

2017年7月5日 星期三

Android元件(AlertDialog) — 簡易客製化Dialog、不同layout的hanlder回調、dip轉px固定高度

前言:

最近看網路上的Code發現有些前輩會使用代碼的方式畫xml。
在還接觸到android studio之後,所有的畫面元件我都是在XML上完成的,使用代碼畫畫面對我來說是一件新鮮的事情。
之後我多多少少都會在範例中使用一些代碼畫xml的方式(像是上一篇的自製側邊欄)。
畢竟,要成功除了1w小時定律,『刻意練習』是成功必須面對的路。

今日實作DEMO




重點程式碼:

在客製化的dialog的class內繼承 AlertDialog
簡易客製化Dialog
1、先畫出custom_dialog.xml
2、寫一個class把custom_dialog寫進去

 View view = LayoutInflater.from(context).inflate(R.layout.activity_dialog, null);
        setView(view);
 

3、在要使用的頁面呼叫他



不同layout的hanlder回調

 private Handler mTextHandler;//INIT Handler
//添加點擊事件
        text.setOnClickListener(onClickListener);
 //設定點擊事件
    View.OnClickListener onClickListener = new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            switch (view.getId()) {
                case R.id.message_dialog:
                    //設定Handler回傳
                    mTextHandler.sendEmptyMessage(0);
                    break;
            }
        }
    };

    //讓其他activity能使用,設定點擊事件
    public void setmTextHandler(Handler handler) {
        this.mTextHandler = handler;
    }

 
dip轉px固定高度
p.s.這個方法在使用code畫畫面的時候常常出現。通常是寫在自定義的application內,求方便範例我就先寫在activity

  //固定高度使用,讓dip轉成px
    public static int dip2px(float dipValue) {
//        Math.round(470 * displayMetrics.density);
        int density = (int) context.getResources().getDisplayMetrics().density;
        return (int) (dipValue * density + 0.5f);
    } 
pixel圖示

 px: pixel就是像素,代表的是螢幕上確切可顯示的點數。
『點』這個字要拿出來特別強調,因為螢幕的解析度就是靠這些點拼湊出來的。
如有兩台相同解析度的手機,但螢幕大小不同,較大螢幕的pixel就會相對較大。
dip:density_independent pixels 他是一種會根據不同螢幕自行轉換的單位。 如果有螢幕得密度是 160像素,此時1dp = 1px但如果螢幕密度變成320像素,此時就會變成1dp = 2px


完整程式碼:

看看今天實作的部分

先來看看activity_main.xml
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.hello.kaiser.handler.MainActivity">

    <TextView
        android:id="@+id/call_dialog"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>

 
中間放一個TextView用來當作點擊跳出Dialog的Button。






畫CustomDialog

activity_dialog.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.hello.kaiser.handler.CustomDialog">

    <TextView
        android:id="@+id/message_dialog"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="我是客製化Dialog" />
</RelativeLayout>

 
activity_dialog.xml 中間放一個TextView用來判別這是客製化的Dialog。


再來畫一個沒做功能的toolbar,筆者在這邊做這功能是想試著自製ToolBar。(完全沒有功能,只是想加) 
activity_toolbar.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.hello.kaiser.handler.CustomToolbar">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="toolbar" />

    <View
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:layout_alignParentBottom="true"
        android:background="@android:color/black" />
</RelativeLayout>

 

接下來先從簡單的寫起,ToolBar因為沒有實質上的功能,所以很簡單,就是把一個View塞入繼承RelativeLayout 的class內。
CustomToolbar.java

public class CustomToolbar extends RelativeLayout {


    public CustomToolbar(Context context) {
        super(context);
        View view = LayoutInflater.from(context).inflate(R.layout.activity_toolbar, this, false);
        addView(view);
    }
}
 
如果在自製的ToolBar上要加上東西,可以把元件寫在這,例如通常在app左上角的漢堡選單之類。
把ToolBar單獨拉出來寫有什麼好處呢?
可以避免在MainActivity內寫入太多的程式碼,顯得臃腫難讀。


接下來寫Custom Dialog囉,重頭戲,解釋我都寫在註解裡了。
CustomDialog
public class CustomDialog extends AlertDialog {
    private TextView text;
    private Handler mTextHandler;//INIT Handler

    protected CustomDialog(@NonNull Context context) {
        super(context);
        //設定View
        View view = LayoutInflater.from(context).inflate(R.layout.activity_dialog, null);
        setView(view);

        text = view.findViewById(R.id.message_dialog);
        text.setText("讀取中....");
        //添加點擊事件
        text.setOnClickListener(onClickListener);
    }
    //設定點擊事件
    View.OnClickListener onClickListener = new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            switch (view.getId()) {
                case R.id.message_dialog:
                    //設定Handler回傳
                    mTextHandler.sendEmptyMessage(0);
                    break;
            }
        }
    };

    //讓其他activity能使用,設定點擊事件
    public void setmTextHandler(Handler handler) {
        this.mTextHandler = handler;
    }

    //本來是設定Dialog上面的Title部分,這裡我把super繼承的地方刪掉,寫來設定text要顯示的字串
    @Override
    public void setMessage(CharSequence message) {
        text.setText(message);
    }
}

 

MainActivity.java
/**
 * 客製化Dialog
 */
public class MainActivity extends Activity {

    private CustomToolbar customToolbar;
    private static Context context;
    private CustomDialog dialog;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        context = this;
        //init 客製化的Toolbar
        customToolbar = new CustomToolbar(this);
        //設定客製化的params,長寬
        RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, dip2px(44));
        //把客製化的toolbar塞入MainActivity的View
        addContentView(customToolbar, params);
        //設定中間文字的點擊事件
        findViewById(R.id.call_dialog).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                //呼叫自定義的客製化Dialog
                dialog = new CustomDialog(context);
                dialog.setMessage("hello loading .....");
                dialog.show();
                //設定客製化的點擊效果
                dialog.setmTextHandler(new Handler() {
                    @Override
                    public void handleMessage(Message msg) {
                        super.handleMessage(msg);
                        Toast.makeText(MainActivity.this, "HelloPress", Toast.LENGTH_SHORT).show();
                    }
                });
            }
        });


    }

    //固定高度使用,讓dip轉成px
    public static int dip2px(float dipValue) {
//        Math.round(470 * displayMetrics.density);
        int density = (int) context.getResources().getDisplayMetrics().density;
        return (int) (dipValue * density + 0.5f);
    }

}

 

DEMO:

https://drive.google.com/open?id=0Byk75IYx-dKXTzlnemF3VUpMRUk


2017年7月4日 星期二

Android實作(DrawerLayout) — 自製抽屜

 前言:

 前幾天想實作一下側邊欄的抽屜。
找了原廠的 drawerLayout,感覺效果不如預期。不夠彈性。
於是我實作了一個抽屜效果。





實作:

今日要實作的地方

程式碼有點多,讓我們先從動畫著手吧。

一、側邊欄動畫效果

二、首頁和側邊欄的製作

三、MainActivity程式的串接




一、側邊欄動畫效果
先在res下創建一個anim 



開兩個檔案,menu_push_in  and menu_push_out

 menu_push_in.xml
  <?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate
        android:duration="250"
        android:fillAfter="true"
        android:fromXDelta="0.0"
        android:toXDelta="+85%p" />
</set>
 
menu_push_out.xml
  
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
    android:duration="250"
    android:fromXDelta="85.0%"
    android:toXDelta="0.0"
    />
</set>
 
/**
*duration 動畫持續時間
*fromXDelta 依賴x軸,動畫起始的地方百分比
*toXDelta 依賴x軸,動畫結束的地方百分比
*/


接下來製作MenuController來實作動畫的效果。
MenuController.java
public class MenuController {


    public static void pushRight(final Context context, final RelativeLayout layout) {
        Animation mAnimIn = AnimationUtils.loadAnimation(context, R.anim.menu_push_in);
        layout.startAnimation(mAnimIn);
        mAnimIn.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {

            }

            @Override
            public void onAnimationEnd(Animation animation) {
                //動畫結束後,Hold住畫面
                TranslateAnimation anim = new TranslateAnimation(0.0f, 0.0f, 0.0f, 0.0f);
                anim.setDuration(1);
                layout.startAnimation(anim);
                int realWidth = (context.getResources().getDisplayMetrics().widthPixels);
                int left = (int) (realWidth * 0.85);
                RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(layout.getLayoutParams());
                params.setMargins(left, 0, -(realWidth + left), 0);
                layout.setLayoutParams(params);

            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });
    }

    public static void pushLeft(final Context context, RelativeLayout layout) {
        Animation mAnimOut = AnimationUtils.loadAnimation(context, R.anim.menu_push_out);
        layout.startAnimation(mAnimOut);
        RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) layout.getLayoutParams();
        params.setMargins(0, 0, 0, 0);
        layout.setLayoutParams(params);

    }
} 
 


二、首頁和側邊欄的製作


直接在畫一個Menu和真正要放內容的Content的Layout。

我的作法是直接把Menu壓在最底部,最上層再用一層ContentLayout蓋著它。

先來製作側邊欄。

menu_main_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/holo_blue_bright"
    tools:context="com.hello.kaiser.drawer.MenuMainLayout">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="我是側邊欄"
        android:textSize="50dp" />
</android.support.constraint.ConstraintLayout>



讓我們來寫menu_main_layout的java部分吧
MenuMainLayout.java
/**
 * Menu內的東西可以實作在這裡
 * */
public class MenuMainLayout extends RelativeLayout {


    public MenuMainLayout(Context context) {
        super(context);
        View view = LayoutInflater.from(context).inflate(R.layout.menu_main_layout, this, false);
        addView(view);
    }
}

 

接著製作首頁內的內容,我是直接在頂端自己寫了ToolBar來呼叫側邊欄。

首頁內容

main_content_layout.xml
 

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#ffffff"
    tools:context="com.hello.kaiser.drawer.MainContentLayout">

    <include layout="@layout/activity_tool_bar" />

</RelativeLayout>


MainContentLayout.java
/***
 *
 * 內容內的東西可以實作在這裏
 *
 * */
public class MainContentLayout extends RelativeLayout {


    public MainContentLayout(Context context) {
        super(context);
        View view = LayoutInflater.from(context).inflate(R.layout.main_content_layout, this, false);
        addView(view);
    }
}

 

三、MainActivity程式的串接

 接著來實作MainActivity吧,來到步驟的最後一步,把上面的東西全部串起來的MagicTime!
MainActivity基本上就只是給一個id,我們要在上面疊入menu和主要content畫面

activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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/main_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.hello.kaiser.drawer.MainActivity">


</RelativeLayout>

 
MainActivity.java
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.RelativeLayout;
import android.widget.TextView;

import com.hello.kaiser.drawer.anim.MenuController;

public class MainActivity extends AppCompatActivity {

    private RelativeLayout main_layout;
    private int RealWidth = 0;
    private TextView mMenuClick;//打開menu的效果
    private boolean MenuOpen = false;//控制menu開啟與否

    RelativeLayout mContent;//主要內容layout
    RelativeLayout mBlack;//menu彈出的黑幕

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //獲取裝置的真正寬度
        RealWidth = getResources().getDisplayMetrics().widthPixels;
        
        initView();
        initListener();
    }


    private void initView() {
        main_layout = (RelativeLayout) findViewById(R.id.main_layout);
        //側邊欄
        MenuMainLayout mMenu = new MenuMainLayout(this);
        //讓側邊欄開啟整個畫面的85%
        main_layout.addView(mMenu, new RelativeLayout.LayoutParams(RealWidth * 85 / 100, ViewGroup.LayoutParams.MATCH_PARENT));

        //主畫面內容
        mContent = new MainContentLayout(this);
        main_layout.addView(mContent, new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));

        //黑幕
        mBlack = new RelativeLayout(this);
        mContent.addView(mBlack, new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
        mBlack.setBackgroundColor(0x7F000000);
        mBlack.setVisibility(View.GONE);

        mMenuClick = (TextView) mContent.findViewById(R.id.menu);

    }

    private void initListener() {
        mBlack.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                menuControl();
            }
        });
        mMenuClick.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                menuControl();
            }
        });
    }

    private void menuControl() {
        if (!MenuOpen) {
            MenuOpen = true;
            mBlack.setVisibility(View.VISIBLE);
            MenuController.pushRight(this, mContent);
        } else {
            MenuOpen = false;
            mBlack.setVisibility(View.GONE);
            MenuController.pushLeft(this, mContent);
        }
    }


}

 

不講解什麼了,其實沒有很難,只是東西有點多,有問題再下發問囉。



Demo:

https://drive.google.com/open?id=0Byk75IYx-dKXa0tWY3RzZkxZYTA

2017年7月3日 星期一

Android元件 — 獲取手機識別碼、版本號碼

前言:

手機唯一識別碼
每一台手機上都是獨一無二的。
應用場景可以在Google上看到,當你在A手機綁定了Google帳號,你只要拿其他手機登入你的帳號,Google馬上會判別這不是你之前常用的A手機的識別碼,然後發訊息提醒你『你有登入訊息唷!是否被盜帳號了呢?』

版本號碼
就是要拿取你的VersionName
通常VersionName是給使用者看的,VersionCode是給開發者自己更新版本確認用的。




重點程式碼:




獲取手機識別碼

 //DeviceId
    @SuppressLint("HardwareIds")
    public static String getDeviceId() {
        UUID uuid = null;
        String androidId = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);
        try {
            uuid = UUID.nameUUIDFromBytes(androidId.getBytes("utf-8"));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return uuid.toString();
    }
  
 
獲取app版本號碼VersionName
   
    //獲取VersionName
    public static String getAppVersion() {
        String version = "";
        PackageManager manager = context.getPackageManager();
        try {
            PackageInfo info = manager.getPackageInfo(context.getPackageName(), 0);
            version = info.versionName;
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
        return version;
    }
    //獲取VersionCode
    public static String getAppVersionNumber() {
        String version = "";
        PackageManager manager = context.getPackageManager();
        try {
            PackageInfo info = manager.getPackageInfo(context.getPackageName(), 0);
            version = String.valueOf(info.versionCode);
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
        return version;
    }
 

通常以上方法是複寫在自己的Application內,因為實作方便,就在Activity內做示範。



實作:


activity_main.xml
  <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.ellipsize.kaiser.version.MainActivity">

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:orientation="vertical">

        <TextView
            android:id="@+id/deivceId"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="deviceiD" />

        <TextView
            android:id="@+id/versionCode"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="deviceiD" />

        <TextView
            android:id="@+id/versionNumber"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="deviceiD" />


    </LinearLayout>


</RelativeLayout>

 
strings.xml
  
<resources>
    <string name="app_name">Version</string>
    <string name="Deviceid">DeviceId : %s</string>
    <string name="VersionName">VersionName : %s</string>
    <string name="VersionCode">VersionCode : %s</string>

</resources>
 

MainActivity.java
  public class MainActivity extends AppCompatActivity {

    public static Context context;

    private TextView DeviceID, versionName, versionCode;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        context = this;
        DeviceID = (TextView) findViewById(R.id.deivceId);
        versionCode = (TextView) findViewById(R.id.versionCode);
        versionName = (TextView) findViewById(R.id.versionNumber);

        DeviceID.setText(getString(R.string.Deviceid, getDeviceId()));
        versionName.setText(getString(R.string.VersionName, getAppVersion()));
        versionCode.setText(getString(R.string.VersionCode, getAppVersionName()));

    }

    //DeviceId
    @SuppressLint("HardwareIds")
    public static String getDeviceId() {
        UUID uuid = null;
        String androidId = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);
        try {
            uuid = UUID.nameUUIDFromBytes(androidId.getBytes("utf-8"));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return uuid.toString();
    }

    public static String getAppVersion() {
        String version = "";
        PackageManager manager = context.getPackageManager();
        try {
            PackageInfo info = manager.getPackageInfo(context.getPackageName(), 0);
            version = info.versionName;
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
        return version;
    }

    public static String getAppVersionName() {
        String version = "";
        PackageManager manager = context.getPackageManager();
        try {
            PackageInfo info = manager.getPackageInfo(context.getPackageName(), 0);
            version = String.valueOf(info.versionName);
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
        return version;
    }
}

 



完成囉!

很簡單就不給Demo了。

協程(coroutine) - 協程為什麼要學它?

 Coroutine 協程 再強調一次 協程就是由kotlin官方所提供的線程api //Thread Thread { }.start() //Executor val execu...