show code block

2017年2月17日 星期五

Android元件:SharedPreferences ─ 儲存資料的好幫手



今日時做影片:





主要程式碼:
SharedPreferences sharedPreferences = getSharedPreferences(String name, int mode);

String name:是你要儲存在手機內的檔案名稱。
Int mode:是你儲存的方式,通常都是使用MODE_PRIVATE(除了你這個app其他app不能使用)

各種MODE
Sr.No
Mode & description
1
MODE_APPEND
This will append the new preferences with the already existing preferences
2
MODE_ENABLE_WRITE_AHEAD_LOGGING
Database open flag. When it is set , it would enable write ahead logging by default
3
MODE_MULTI_PROCESS
This method will check for modification of preferences even if the sharedpreference instance has already been loaded
4
MODE_PRIVATE
By setting this mode, the file can only be accessed using calling application
5
MODE_WORLD_READABLE
This mode allow other application to read the preferences
6
MODE_WORLD_WRITEABLE
This mode allow other application to write the preferences


儲存方式:
SharedPreferences sharedPreferences = getSharedPreferences(data, MODE_PRIVATE);

假設你在EditText內打了一串東西想之後打開app之後還留存。


sharedPreferences.edit()
     .putString(“I_WANT_TO_SAVE_EDIT”, OneEdit.getText().toString())
     .apply();

呼叫sharedPreferences在後面加一個eidt()  (英文大補帖: edit 編輯 )
仔使用putstring 加入你要處存的東西
apply 使它生效(英文大補帖: apply  始生效 )
這就存起來了。
它會存在叫“I_WANT_TO_SAVE_EDIT”的檔案內。

讀取方式:


OneEdit.setText(sharedPreferences.getString(“I_WANT_TO_SAVE_EDIT”, ""));




同場加映

固定Switch開關的問題。

當我讓SWITCH開關成開啟狀態,當下次開啟APP時,要如何保持在開啟狀態呢?


當然是先在xml 內畫一個 switchButton


activity_main.xml
  <Switch
        android:id="@+id/switch_button"
        android:layout_marginTop="10dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal" />

MainActivity.java

先設定元件
private Switch switch_buton;

指定給xml
switch_buton = (Switch) findViewById(R.id.switch_button);

設定SwitchButton的點擊方式

 switch_buton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (switch_buton.isChecked()) {
                    //如果SwitchButton開啟 do something

                    //儲存
                    sharedPreferences.edit().putBoolean(switchField, true).apply();
                    Log.d(handle, "onClick isChecked switchField= is Open");
                } else {
                    //如果SwitchButton關閉 do something

                    //儲存
                    sharedPreferences.edit().putBoolean(switchField, false).apply();
                    Log.d(handle, "onClick isClosed switchField= is Closed");
                }
            }
        });

然後再OnCreate的地方丟入,這行是指你開啟APP時,讀取你存在sharedPreferences內的SWITCH 狀態。
switch_buton.setChecked(sharedPreferences.getBoolean(switchField, false));

這樣SwitchButton就可以正常開關囉!




完整程式碼:
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.chat.a015865.sharedpreferences.MainActivity">

    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="1" />

    <EditText
        android:id="@+id/oneField"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
      />

    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="2" />

    <EditText
        android:id="@+id/twoField"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
       />

    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="3" />

    <EditText
        android:id="@+id/threeField"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
       />

    <Switch
        android:id="@+id/switch_button"
        android:layout_marginTop="10dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal" />

    <Button
        android:id="@+id/close_button"
        android:text="關閉app"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</LinearLayout>



MainActivity.java
public class MainActivity extends AppCompatActivity {

    private static final String handle = MainActivity.class.getSimpleName();

    private EditText OneEdit, TWO, THREE;
    private Context context;
    private Button close_Button;
    private Activity activity;

    private SharedPreferences sharedPreferences;
    private static final String data = "DATA";
    private static final String helloField = "ONE_STATE";
    private static final String twoField = "TWO_STATE";
    private static final String threeField = "THREE_SATE";
    private static final String switchField = "SWITCH_STATE";

    private Switch switch_buton;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        context = this;
        activity = this;
        initView();
        initSet();
        readData();//讀取
    }

    private void initView() {
        close_Button = (Button) findViewById(R.id.close_button);
        sharedPreferences = getSharedPreferences(data, MODE_PRIVATE);
        switch_buton = (Switch) findViewById(R.id.switch_button);
        OneEdit = (EditText) findViewById(R.id.oneField);
        TWO = (EditText) findViewById(R.id.twoField);
        THREE = (EditText) findViewById(R.id.threeField);
    }

    private void initSet() {
        switch_buton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (switch_buton.isChecked()) {
                    //如果SwitchButton開啟 do something

                    //儲存
                    sharedPreferences.edit().putBoolean(switchField, true).apply();
                    Log.d(handle, "onClick isChecked switchField= is Open");
                } else {
                    //如果SwitchButton關閉 do something

                    //儲存
                    sharedPreferences.edit().putBoolean(switchField, false).apply();
                    Log.d(handle, "onClick isClosed switchField= is Closed");
                }
            }
        });

        close_Button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                finish();
            }
        });
    }

    public void readData() {//讀取
        switch_buton.setChecked(sharedPreferences.getBoolean(switchField, false));
        OneEdit.setText(sharedPreferences.getString(helloField, ""));
        TWO.setText(sharedPreferences.getString(twoField, ""));
        THREE.setText(sharedPreferences.getString(threeField, ""));
    }

    public void saveData() {//儲存
        sharedPreferences.edit()
                .putString(helloField, OneEdit.getText().toString())
                .putString(twoField, TWO.getText().toString())
                .putString(threeField, THREE.getText().toString())
                .apply();
    }


    @Override
    protected void onPause() {
        super.onPause();
        Log.d(handle, "onPause()");
        saveData();
    }

    @Override
    protected void onStop() {
        super.onStop();
        Log.d(handle, "onStop()");
        saveData();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.d(handle, "onDestroy()");
        saveData();
    }
}





Demo下載:
https://drive.google.com/open?id=0Byk75IYx-dKXMDZZcHFHY3U4SXc
有問題請發問囉!


2017年2月16日 星期四

third-party元件:ImageLoader(網路下載圖片 imageLoader, 封裝)

網路下載圖片 imageLoader

官方檔案:




前言:
In short, 網路上很多資訊,稍微講解一下我覺得重要的部分。
我從網路上抓圖片都使用這一套,防止APP oom的好幫手。
為什麼我要自己拉出來做一個Tools呢?這樣不是脫褲子放屁嗎?
因為在一個專案裡面,你一定會大量的用到某些元件,這時候就可以把你重複用到的原件自己封裝成一包寫進Tools內。
ImageLoader也是常用到的一員,就拉出來自己寫在tools內了。



實作:
 build.gradle (Module: app) 加上

compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.5'

以下是整個Tools.java

public class Tools {
   private static final String handle = Tools.class.getSimpleName();

   public void imageLoading(Context context, String url, ImageView imageView) {
       ImageLoaderConfiguration config = new ImageLoaderConfiguration
               .Builder(context)
               .memoryCacheExtraOptions(480, 800) //保存每個緩存圖片的最大寬高
               .threadPriority(Thread.NORM_PRIORITY - 2) //線池中的緩存數
               .denyCacheImageMultipleSizesInMemory() //禁止緩存多張圖片
               .memoryCache(new FIFOLimitedMemoryCache(2 * 1024 * 1024))//缓存策略
//                .memoryCacheSize(50 * 1024 * 1024) //設置內存緩存的大小
               .diskCacheFileNameGenerator(new Md5FileNameGenerator()) //缓存文件名的保存方式
//                .diskCacheSize(200 * 1024 * 1024) //緩存大小
               .tasksProcessingOrder(QueueProcessingType.LIFO) //工作序列
               .diskCacheFileCount(200) //緩存的文件數量
               .build();
       if (!ImageLoader.getInstance().isInited()) {//偵測如果imagloader已經init,就不再init
           Log.d(handle, "CheckPoint init");
           ImageLoader.getInstance().init(config);
       }
       ImageLoader.getInstance().displayImage(url, imageView, ImageUsing);

   }
//    UsingFreqLimitedMemoryCache(如果緩存的圖片總量超過限定值,先刪除使用頻率最小的bitmap)
//    LRULimitedMemoryCache(這個也是使用的lru算法,和LruMemoryCache不同的是,他緩存的是bitmap的弱引用)
//    FIFOLimitedMemoryCache(先進先出的緩存策略,當超過設定值,先刪除最先加入緩存的bitmap)
//    LargestLimitedMemoryCache(當超過緩存限定值,先刪除最大的bitmap對象)
//    LimitedAgeMemoryCache(當bitmap加入緩存中的時間超過我們設定的值,將其刪除)

//    Universal-Image-Loader的硬盤緩存策略
//    詳細的硬盤緩存策略可以移步:http://blog.csdn.net/xiaanming/article/details/27525741,下方是總結的結果:

//    FileCountLimitedDiscCache(可以設定緩存圖片的個數,當超過設定值,刪除掉最先加入到硬盤的文件)
//    LimitedAgeDiscCache(設定文件存活的最長時間,當超過這個值,就刪除該文件)
//    TotalSizeLimitedDiscCache(設定緩存bitmap的最大值,當超過這個值,刪除最先加入到硬盤的文件)
//    UnlimitedDiscCache(這個緩存類沒有任何的限制)
  
   DisplayImageOptions ImageUsing = new DisplayImageOptions.Builder()
           .showImageOnLoading(R.drawable.loading)//圖片還沒下載好時跑的臨時圖片
           .showImageForEmptyUri(R.drawable.loading)
           .showImageOnFail(R.drawable.loading).cacheInMemory(true)//緩存
           .cacheOnDisc(true).bitmapConfig(Bitmap.Config.RGB_565)
           .imageScaleType(ImageScaleType.IN_SAMPLE_INT)
           .build();
}
//.showImageOnLoading(R.drawable.loading)//圖片還沒下載好時跑的臨時圖片
//.showImageOnFail(R.drawable.loading)//圖片讀取失敗時跑的臨時圖片


所以請在drawable內放入您自己想放入的圖片。



使用方式:
        /**
         * imageLoading(Context context, String url, ImageView imageView)
         * context就是context;
         * url:你需要讀取圖片的網址
         * imageview:你指定findViewById的物件
         */

        tools.imageLoading(context, url,  imageView);

當然你也可以使用
ImageLoader.getInstance().displayImage(url, imageView, ImageUsing);




完整程式碼:

activity_main.xml



activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.chat.a015865.imageloderdemo.MainActivity">

    <ImageView
        android:layout_centerInParent="true"
        android:id="@+id/show"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</RelativeLayout>


MainActivity.java

public class MainActivity extends AppCompatActivity {

    private ImageView imageView;
    private Tools tools=new Tools();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
        initSet();
    }
    private void initView() {
        imageView = (ImageView)findViewById(R.id.show);
    }
    private void initSet() {
        String url = "https://www.cloudnloud.com/wp-content/uploads/2016/07/podcast-ac-new_2.jpg";
        tools.imageLoading(this, url, imageView);
    }
}


Tools.java

public class Tools {
    private static final String handle = Tools.class.getSimpleName();

    public void imageLoading(Context context, String url, ImageView imageView) {
        ImageLoaderConfiguration config = new ImageLoaderConfiguration
                .Builder(context)
                .memoryCacheExtraOptions(480, 800) //保存每個緩存圖片的最大寬高
                .threadPriority(Thread.NORM_PRIORITY - 2) //線池中的緩存數
                .denyCacheImageMultipleSizesInMemory() //禁止緩存多張圖片
                .memoryCache(new FIFOLimitedMemoryCache(2 * 1024 * 1024))//缓存策略
//                .memoryCacheSize(50 * 1024 * 1024) //設置內存緩存的大小
                .diskCacheFileNameGenerator(new Md5FileNameGenerator()) //缓存文件名的保存方式
//                .diskCacheSize(200 * 1024 * 1024) //緩存大小
                .tasksProcessingOrder(QueueProcessingType.LIFO) //工作序列
                .diskCacheFileCount(200) //緩存的文件數量
                .build();
        if (!ImageLoader.getInstance().isInited()) {//偵測如果imagloader已經init,就不再init
            Log.d(handle, "CheckPoint init");
            ImageLoader.getInstance().init(config);
        }
        ImageLoader.getInstance().displayImage(url, imageView, ImageUsing);

    }
    DisplayImageOptions ImageUsing = new DisplayImageOptions.Builder()
            .showImageOnLoading(R.drawable.loading)
            .showImageForEmptyUri(R.drawable.loading)
            .showImageOnFail(R.drawable.loading).cacheInMemory(true)//緩存
            .cacheOnDisc(true).bitmapConfig(Bitmap.Config.RGB_565)
            .imageScaleType(ImageScaleType.IN_SAMPLE_INT)
            .build();
}


最後別忘了,加權限。 TMD寫Demo被這雷了十分鐘。QQ

<uses-permission android:name="android.permission.INTERNET"/>




Demo下載:
https://drive.google.com/open?id=0Byk75IYx-dKXWFNjMFRoQ3N2Rkk

third-party元件 ─ 第三方AsyncHttpClient(使用方式和封裝)


請先參考:此篇https://disp.cc/b/11-9faq

此時你會發現我網誌就到這邊結束了,因為裡面都講得很詳細。

好啦當然開玩笑的。

也稍微介紹一下,基本上就是複製貼上。後面我會把這個方法做個封裝。

build.gradle (Module: app) 加上
   
compile 'com.loopj.android:android-async-http:1.4.9' 


此時最主要的方法是以下兩個
AsyncHttpClient client = new AsyncHttpClient(); 
RequestParams params = new RequestParams();





讓我們開始吧。

先加權限!

以下是重點程式碼的實作


String url = "http://disp.cc/api/board.php";
AsyncHttpClient client = new AsyncHttpClient();
RequestParams params = new RequestParams();
params.put("act", "blist");
params.put("limitNum", "5");
client.get(url, params, new JsonHttpResponseHandler() {
    @Override
    public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
        // Root JSON in response is an dictionary i.e { "data : [...] }
        // Handle resulting parsed JSON response here
        if (response.optInt("isSuccess") != 1) {
            Toast.makeText(mContext, "Error:" + response.optString("errorMessage"), Toast.LENGTH_LONG).show();
            return;
        }
        JSONObject data = response.optJSONObject("data");
        int totalNum = data.optInt("totalNum");
        Log.d("test","totalNum: " + totalNum);
    }

    @Override
    public void onFailure(int statusCode, Header[] headers, String res, Throwable t) {
        // called when response HTTP status is "4XX" (eg. 401, 403, 404)
        Toast.makeText(getApplicationContext(), "Error: " + statusCode + " " + e.getMessage(), Toast.LENGTH_LONG).show();
    }
});



要讀取網址:http://disp.cc/api/board.php?act=blist&limitNum=5

如何讀取:
String url = "http://disp.cc/api/board.php";
params.put("act", "blist");
params.put("limitNum", "5");
這是get的用法

如果要使用post 就可以不用加params 直接
String url = " http://disp.cc/api/board.php?act=blist&limitNum=5";
就可以用了。
至於postget的差別在哪? 大概可以理解成post的安全性比較高。

此時上面網址的json檔案長這樣:


isSuccess的部分為1,如果不等於1(response.optInt("isSuccess") != 1 ),就會跳Json內的errorMessage





接下來文章內的東西大致上介紹完畢了。


我想對AsynHttpClient做封包。


我不希望每次調用都是整個方法重新寫一次。
我希望以後我讀取資料可以統一變成以下這樣。

我希望把Url 和  params包成同一個東西。
紅框標起來的為我想呈現的程式碼。







接下來就來實作吧。

CommunicationManager.java
 public class CommunicationManager {
    public static final String handle = CommunicationManager.class.getSimpleName();
    private static CommunicationManager instance = new CommunicationManager();//變成靜態
    private static AsyncHttpClient client = new AsyncHttpClient();

    static {
        client.setTimeout(10000);
        client.setResponseTimeout(10000);
        client.setEnableRedirects(true, true, true);
    }

    private CommunicationManager() {//要件
    }

    public static CommunicationManager getInstance() {//變成靜態用
        return instance;
    }

    public void post(Context context, RequestData RequestData, AsyncHttpResponseHandler asyncHttpResponseHandler) {
        client.get(context, RequestData.getUrl(), RequestData.getHttpParams(), asyncHttpResponseHandler);
    }

    public static <T> T covertObj(String content, Class<T> classOfT) {
        Gson gson = new Gson();
        T obj = null;
        try {
            obj = gson.fromJson(content, classOfT);
        } catch (JsonSyntaxException e) {
            Log.d(handle, "Failed to convert gson:" + content);
            e.printStackTrace();
        }
        return obj;
    }

} 


這是拿來做這件事情的





RequestData.java
/**
 * 二次封包
 */

public class RequestData {
    public static final String handle = RequestData.class.getSimpleName();
    private Builder builder;

    private RequestData(Builder builder) {
        this.builder = builder;
    }

    public String getUrl() {
        return builder.url;
    }

    public List getParameters() {
        return builder.parameters;
    }

    public RequestParams getHttpParams() {
        RequestParams params = new RequestParams();
        for (int i = 0; i < getParameters().size(); i++) {
            NameValuePair pair = getParameters().get(i);
            params.add(pair.getName(), pair.getValue());
        }
        return params;
    }


    public static class Builder {

        String url;
        List parameters = new ArrayList<>();

        public Builder(String url) {
            this.url = url;
            parameters = new ArrayList<>();
        }

        public Builder addParameter(String name, String value) {
            BasicNameValuePair nameValuePair = new BasicNameValuePair(name, value);
            parameters.add(nameValuePair);
            return this;
        }

        public RequestData build() {
            return new RequestData(this);
        }
    }
}


大致完成了。






此時我們可以在MainActivity直接呼叫我們封包過的AsynHttpClient
就可以直接這樣呼叫使用
 private void initAsynHttp() {
        // http://disp.cc/api/board.php?act=blist&limitNum=5
        String url = "http://disp.cc/api/board.php";
        final RequestData requestData = new RequestData.Builder(url)
                .addParameter("act", "blist")
                .addParameter("limitNum", "5")
                .build();
        CommunicationManager.getInstance().post(context, requestData, new TextHttpResponseHandler() {
            @Override
            public void onFailure(int statusCode, Header[] headers, String responseString, Throwable throwable) {
            //失敗的事情,responseString內你會看到他跳出的錯誤訊息
              Log.d("OnFailer","OnFailer"+responseString);
   }

            @Override
            public void onSuccess(int statusCode, Header[] headers, String responseString) {
             //成功後做的事情,responseString內你會看到整個json檔案
                Log.d("onSuccess","onSuccess"+responseString);
            }
        });
    } 


此時會想問,要如何讀取json內的資料呢?

其實你已經寫好轉換方式了。

就是在CommunicationManager內的這一段:
    
public static <T> T covertObj(String content, Class<T> classOfT) {
        Gson gson = new Gson();
        T obj = null;
        try {
            obj = gson.fromJson(content, classOfT);
        } catch (JsonSyntaxException e) {
            Log.d(handle, "Failed to convert gson:" + content);
            e.printStackTrace();
        }
        return obj;
    }



此時你回到json檔案,會發現你的json有三層。
開始創建三層java呼應的json檔案

第一層:
public class JsonFirst implements Serializable {
    @SerializedName("isSuccess")
    public int isSuccess;

    @SerializedName("errorMessage")
    public String errorMessage;

    @SerializedName("data")
    public data dataLists;
}

第二層:
public class data {
    @SerializedName("isLogin")
    public boolean isLogin;

    @SerializedName("totalNum")
    public int totalNum;

    @SerializedName("remainNum")
    public int remainNum;

    @SerializedName("blist")
    public List blistLists;
}//最後一個blist json檔內是一個陣列,所以必須給一個List

第三層:
public class blist implements Serializable {
    public int bi;
    public String name;
    public String type;
    public String title;
    public String icon;
    public String hot;
}

創建好之後,就可以直接呼叫您的json檔案出來了
JsonFirst getJsonFirst = CommunicationManager.covertObj(responseString, JsonFirst.class);


你可以設Log看看是否呼喚成功。
private void initAsynHttp() {

    // http://disp.cc/api/board.php?act=blist&limitNum=5

    String url = "http://disp.cc/api/board.php";

    final RequestData requestData = new RequestData.Builder(url)
            .addParameter("act", "blist")
            .addParameter("limitNum", "5")
            .build();

    Log.d(handle, "RequestData =" + requestData.getUrl() + "\n Params :" + requestData.getParameters());
    CommunicationManager.getInstance().post(context, requestData, new TextHttpResponseHandler() {

        @Override

        public void onFailure(int statusCode, Header[] headers, String responseString, Throwable throwable) {
            Log.d(handle, " onFailure " + responseString + "\nthrowable =" + throwable);
            Toast.makeText(MainActivity.this, "responseString =" + responseString + "\n throwable =" + throwable, Toast.LENGTH_SHORT).show();
        }



        @Override
        public void onSuccess(int statusCode, Header[] headers, String responseString) {
            Log.d(handle, " onSuccess " + requestData.getUrl());
            JsonFirst getJsonFirst = CommunicationManager.covertobj(responseString, JsonFirst.class);
            Log.d(handle, "CheckPoint.dataLists =" + getJsonFirst.dataLists.blistLists.size());
            Log.d(handle, "CheckPoint getJsonFirst.isSuccess =" + getJsonFirst.isSuccess);
        }
    });
}
 


之後再做ListView 或是 RecyclerView把陣列的資料列出來就大功告成囉。



內容有點多,寫的比較粗略,有不懂在提問吧。

檔案Demo打包:
https://drive.google.com/open?id=0Byk75IYx-dKXMGQ5VlZyM3hVbjA

2017年2月7日 星期二

android小帖步 - sha1金鑰

android小帖步 - sha1金鑰


前言:

sha1金鑰找的方式千百種,Google就一堆方式。



不用在使用以下方式了
keytool -list -v -keystore C:\Users\PC\.android\debug.keystore
這套公式真的不是人人適用,我使用上面的就無法,當時弄得我很頭痛!

當時是使用這種方式來時作。

這幾天誤打誤撞發現了一個最快的方式。讓我們開始吧!





實作:



 當你開啟新專案時,選擇GoogleMapsActivity(上圖)



進入專案後,你就會神奇的發現,sha1 key出現拉!





要找sha1 key就是那麼簡單!


2017年2月6日 星期一

會伸縮的圖片(draw9patch、對話框的製作)

會伸縮的圖片(9patch)

前言:
9patch是可以拿來解決圖片拉伸問題所出現的東西。
假如我要做一個對話框,對話框要如何配合裡面的文字變長、變寬?
這時候就要用到我們的draw9patch.bat








使用:
draw9patch.bat通常在你的Android\sdk\tools內你會發現它。
如果真的找不到,請打開你的搜索bar直接輸入draw9patch.bat(下圖)



出現此畫面請不要關閉它,就算程式已經開啟也不要關閉,它們是唇齒相依的,關一個死兩個。(下圖)


在此開啟檔案(下圖),選擇你要實作的圖片。






 開啟後就先來看圖說故事吧。(下圖)


真的要注意兩邊等長問題,不然Android會報錯誤。做白工!


完成之後左上角按下File > save 就完成囉。(下圖)




網路上會有些人說要使用去邊軟體,如果你是單純要Android 開發用,完全沒有必要。因為丟在程式內它會自動幫你忽略四周的黑線,當然如果你覺得礙眼也是可以使用xUltimate-d9pc-x86去四周的黑線,不引響。

如何使用呢?


android:background="@drawable/helloman" 

把剛放好的9patch圖片改名為helloman,設成TextView的background,你就會發現圖片會跟著內容的文字做延伸囉!
注意我改的名稱為helloman.9.png  後面的.9.png不能拿掉。不然會失去效果。


以下是這次程式的Demo範例
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.chat.a015865.ninepatchdemo.MainActivity">

    <TextView
        android:id="@+id/textshow_xml"
        android:text="What do u want to Say?"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:background="@drawable/helloman" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:orientation="horizontal">

        <EditText
            android:id="@+id/Edit_xml"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="0.8" />

        <Button
            android:id="@+id/enter_xml"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="0.2"
            android:text="輸入" />
    </LinearLayout>

</RelativeLayout>

MainActivity.java

public class MainActivity extends AppCompatActivity {

    private Button mEnter;
    private EditText mEditText;
    private TextView mTextViewShow;

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

        initView();

    }

    private void initView() {
        mEditText = (EditText) findViewById(R.id.Edit_xml);
        mEnter = (Button) findViewById(R.id.enter_xml);
        mTextViewShow = (TextView) findViewById(R.id.textshow_xml);
        mEnter.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String getContent = mEditText.getText().toString();
                mTextViewShow.setText(getContent);
            }
        });
    }
}




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

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