show code block

顯示具有 third-party元件(第三方) 標籤的文章。 顯示所有文章
顯示具有 third-party元件(第三方) 標籤的文章。 顯示所有文章

2017年4月20日 星期四

third-party元件(XZing.core) ─ 生成QRCode、導入第三方(jar)

前言:

有別於掃描的用法。

這次是在App內生成QRCode。

情境可以是給她一個網址參數,或任何字串,然後產生QRcode提供掃描。

像是購物網站,當你買了物品,你想追蹤商品情況時,購物網站可以在訂單查詢內提供QRCode供你掃描查看商品跑去哪了。
可以看看這第三方 https://github.com/zxing/zxing







實作:

等等要操作到的地方



STEP 1 :先匯入專案


兩種匯入方式 兩種選擇其中一種就好,別傻傻的兩個都匯入
第一種
第一種是直接compile
compile 'com.google.zxing:core:3.2.1'
 

 第二種
先把core-3.2.1.jar下載下來。

然後就開始匯入專案了。

匯入專案的地方可以參考這一篇:
http://nikeru8.blogspot.tw/2017/03/androidyoutube-api-youtube-api.html

我在這邊使用另外一種方式,不過大同小異。

下載下來後,把core-3.2.1.jar放到你自己專案的資料夾libs內。(如果沒有此資料夾請自己創)

如果圖片太小可點擊放大
core-3.2.1.jar放入後讓我們回到Android Studio,匯入專案囉

點選左上角選擇「Project」>>  找到「libs」>> 找到「core-3.2.1.jar」 >> 點擊右鍵 >> 選擇「Add As Library...



跳出視窗選擇「app」 >> ok


專案匯入完成囉!


STEP 2:來畫我們的xml

activity_main.xml
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:id="@+id/button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:clickable="true"
        android:gravity="center"
        android:onClick="onbuttonclick"
        android:text="產生QRCode" />

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/button"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="45dp" />
</RelativeLayout>


設置一個Button,等點擊之後生成QRCode


STEP 3:寫Code in JAVA

MainActivity.java
public class MainActivity extends AppCompatActivity {

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

    public void onbuttonclick(View view) {
        String QRCodeContent = "http://nikeru8.blogspot.tw/2017/04/third-partyxzingcore-qrcodejar.html";
        int QRCodeWidth = 200;
        int QRCodeHeight = 200;

        //QRCode內容編碼
        Map hints = new EnumMap(EncodeHintType.class);
        hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");

        MultiFormatWriter writer = new MultiFormatWriter();
        try {
            //ErrorCorrectionLevel容錯率分四級:L(7%) M(15%) Q(25%) H(30%)
            hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);

            //建立QRCode的資料矩陣
            BitMatrix result = writer.encode(QRCodeContent, BarcodeFormat.QR_CODE, QRCodeWidth, QRCodeHeight, hints);

            //建立矩陣圖
            Bitmap bitmap = Bitmap.createBitmap(QRCodeWidth, QRCodeHeight, Bitmap.Config.ARGB_4444);
            for (int y = 0; y < QRCodeHeight; y++) {
                for (int x = 0; x < QRCodeWidth; x++) {
                    bitmap.setPixel(x, y, result.get(x, y) ? Color.BLACK : Color.WHITE);
                }
            }

            //設定給xml
            ImageView imageView = (ImageView) findViewById(R.id.imageView);
            imageView.setImageBitmap(bitmap);
        } catch (WriterException e) {
            e.printStackTrace();
        }
    }
}

完成囉!
QRCodeContent內就是QRCode產生出來的字串。


eidt(2017/06/12)
我發現如果QRCode你要放到很大很大,直接調整QRCodeWidth和Height會因為產生bitmap而有卡頓狀況。
此時可以用一個方法改善
  bitmap = Bitmap.createScaledBitmap(bitmap,
                    bitmap.getWidth() * 4,
                    bitmap.getHeight() * 4, false);


eidt(2017/11/27)
兩件事
一、控制QRCode旁邊白邊的大小
hints.put(EncodeHintType.MARGIN, 0);
 
後面的0 這個int參數,設得越大白邊就越寬
二、QRCode圖片太大有時候會造成app卡頓
使用AsyncTask http://nikeru8.blogspot.tw/2017/11/androidasynctask_26.html




文獻:
http://gnehcic.azurewebsites.net/android%E5%BB%BA%E7%AB%8Bqr-code/
https://github.com/zxing/zxing


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


2017年3月22日 星期三

third-party元件(Youtube API) ─ Youtube API 簡易使用(二) 控制元件、移除SeekBar、自訂按鈕

前言:


今天來講一下YoutubePlay的控制方式。

想要自己控制PLAY、PAUSE按鈕,又或者想要自訂SeekBar。

這時候就必須要一個乾淨純粹的View ─

除了一個Time Bar 和點擊畫面上的Play、Pause外,是不是很乾淨的,很純粹。


其實也可以把所有控制按鈕都拿掉,所有的控制元件都自己設定,就像MixerBox一樣。

MixerBox就是自己控制所有元件的最好例子,單看畫面還不覺得這出自Youtube!

先讓我們看看Youtube官方文件。
https://developers.google.com/youtube/android/player/reference/com/google/android/youtube/player/YouTubePlayer.PlayerStyle

在裡面有一個mothed是專門控制Style的



YouTubePlayer.PlayerStyle

java.lang.Object
   ↳java.lang.Enum<E extends java.lang.Enum<E>>
   ↳com.google.android.youtube.player.YouTubePlayer.PlayerStyle

Overview

The different styles available for the player.

Summary

Enum values
YouTubePlayer.PlayerStyle.CHROMELESSA style that shows no interactive player controls.
YouTubePlayer.PlayerStyle.DEFAULTThe default style, showing all interactive controls.
YouTubePlayer.PlayerStyle.MINIMALA minimal style, showing only a time bar and play/pause controls.
Public methods
static YouTubePlayer.PlayerStylevalueOf(String name)
final static PlayerStyle[]values()


public static final YouTubePlayer.PlayerStyle CHROMELESS

A style that shows no interactive player controls.
這個風格完全移除和畫面的互動,所有東西都必須自訂。
ex:就如同MixerBox一樣,自訂所有畫面

public static final YouTubePlayer.PlayerStyle DEFAULT

The default style, showing all interactive controls.
正常版風格,保留了一切你在Youtube上面看的到的控制元件,包含點它們右下角的Youtube可直接跳轉到它們app觀看的原件。

public static final YouTubePlayer.PlayerStyle MINIMAL

A minimal style, showing only a time bar and play/pause controls.
最小簡化的風格,只保留TimeBar和螢幕上的播放和暫停控制元件。
ex:就如同我前言那張圖一樣,可以明顯看出pause暫停按鈕和下面的TimeBar




程式碼:

先前的程式碼請先參考:http://nikeru8.blogspot.tw/2017/03/androidyoutube-api-youtube-api.html

我們加在讀取成功後的方法內。

onInitializationSuccess


當然要讀取成功後控制這一切才有意義XDD
 @Override
    public void onInitializationSuccess(YouTubePlayer.Provider provider, YouTubePlayer youTubePlayer, boolean wasRestored) {
        Toast.makeText(this, "onInitializationSuccess!", Toast.LENGTH_SHORT).show();
        if (youTubePlayer == null) {
            Log.d("CheckPoint", "CheckPoint youtubePlayer == null");
            return;
        }
        // Start buffering
        if (!wasRestored) {
            Log.d("CheckPoint", "CheckPoint !wasRestored");
            youTubePlayer.cueVideo(VIDEO_ID);
        }
//        youTubePlayer.setPlayerStyle(YouTubePlayer.PlayerStyle.CHROMELESS);//移除全部控制按鈕
//        youTubePlayer.setPlayerStyle(YouTubePlayer.PlayerStyle.DEFAULT);//原版
        youTubePlayer.setPlayerStyle(YouTubePlayer.PlayerStyle.MINIMAL);//移除下面的控制按鈕
    }


這邊比較值得注意的是,使用PlayerStyle.CHROMELESS時,因為移除了全部的控制按鈕,你甚至不能按下Play按鈕,要請你自己添加控制元件囉。
不然就會像一張圖片一樣卡在那。




控制元件Code:

如上所述,先在onInitializationSuccess內上程式碼。

youTubePlayer.setPlayerStyle(YouTubePlayer.PlayerStyle.CHROMELESS);



再來改變main_activity.xml的布局

main_activity.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:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.chat.a015865.youtube.MainActivity">

    <com.google.android.youtube.player.YouTubePlayerView
        android:id="@+id/player_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <LinearLayout
        android:id="@+id/video_control"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/player_view"
        android:background="#444"
        android:gravity="center_vertical"
        android:orientation="horizontal"
        android:visibility="visible"
        android:weightSum="10"
        tools:layout_editor_absoluteX="84dp"
        tools:layout_editor_absoluteY="374dp">

        <ImageButton
            android:id="@+id/play_video"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_margin="5dp"
            android:layout_weight="1"
            android:background="@null"
            android:src="@drawable/ic_play" />

        <ImageButton
            android:id="@+id/pause_video"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_margin="5dp"
            android:layout_weight="1"
            android:background="@null"
            android:src="@drawable/ic_pause" />

        <SeekBar
            android:id="@+id/video_seekbar"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_margin="10dp"
            android:layout_weight="6"
            android:max="100"
            android:progress="0" />

        <TextView
            android:id="@+id/play_time"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_margin="5dp"
            android:layout_weight="2"
            android:text="--:--"
            android:textColor="@android:color/white" />
    </LinearLayout>
</RelativeLayout>





MainActivity.java

public class MainActivity extends YouTubeBaseActivity implements YouTubePlayer.OnInitializedListener, View.OnClickListener {


    public static final String API_KEY = "AIzaSyAPThx6WbAxjnX2En0qtf9OhD80tUcp380";

    //https://www.youtube.com/watch?v=
    public static final String VIDEO_ID = "qZIWO9TqvIA";

    private YouTubePlayer mYoutubePlayer;
    private View mPlayButtonLayout;
    private TextView mPlayTimeTextView;

    private Handler mHandler = null;
    private SeekBar mSeekBar;


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

        final YouTubePlayerView mYoutubePlayerView = (YouTubePlayerView) findViewById(R.id.player_view);
        mYoutubePlayerView.initialize(API_KEY, this);
        initView();
        mPlayButtonLayout = findViewById(R.id.video_control);
        findViewById(R.id.play_video).setOnClickListener(this);
        findViewById(R.id.pause_video).setOnClickListener(this);

        mPlayTimeTextView = (TextView) findViewById(R.id.play_time);
        mSeekBar = (SeekBar) findViewById(R.id.video_seekbar);
        mSeekBar.setOnSeekBarChangeListener(mVideoSeekBarChangeListener);
        mHandler = new Handler();
    }

    private void initView() {

    }

    @Override
    public void onInitializationSuccess(YouTubePlayer.Provider provider, YouTubePlayer youTubePlayer, boolean wasRestored) {
        Toast.makeText(this, "onInitializationSuccess!", Toast.LENGTH_SHORT).show();
        mYoutubePlayer = youTubePlayer;
        if (youTubePlayer == null) {
            Log.d("CheckPoint", "CheckPoint youtubePlayer == null");
            return;
        }
        // Start buffering
        if (!wasRestored) {
            Log.d("CheckPoint", "CheckPoint !wasRestored");
            youTubePlayer.cueVideo(VIDEO_ID);
        }
        youTubePlayer.setPlayerStyle(YouTubePlayer.PlayerStyle.CHROMELESS);
//        youTubePlayer.setPlayerStyle(YouTubePlayer.PlayerStyle.DEFAULT);//原版
//        youTubePlayer.setPlayerStyle(YouTubePlayer.PlayerStyle.MINIMAL);//移除下面的控制按鈕

        youTubePlayer.setPlayerStateChangeListener(mPlayerStateChangeListener);//影片在run時的狀態
        youTubePlayer.setPlaybackEventListener(mPlaybacckEventListener);
    }

    YouTubePlayer.PlaybackEventListener mPlaybacckEventListener = new YouTubePlayer.PlaybackEventListener() {
        @Override
        public void onPlaying() {
            mHandler.postDelayed(runnable, 100);
            displayCurrentTime();
        }

        @Override
        public void onPaused() {
            mHandler.removeCallbacks(runnable);
        }

        @Override
        public void onStopped() {
            mHandler.removeCallbacks(runnable);
        }

        @Override
        public void onBuffering(boolean b) {

        }

        @Override
        public void onSeekTo(int i) {
            Log.d("CheckPoint", "CheckPoint i = " + i);
            mHandler.postDelayed(runnable, 100);
        }
    };
    YouTubePlayer.PlayerStateChangeListener mPlayerStateChangeListener = new YouTubePlayer.PlayerStateChangeListener() {
        @Override
        public void onLoading() {

        }

        @Override
        public void onLoaded(String s) {

        }

        @Override
        public void onAdStarted() {

        }

        @Override
        public void onVideoStarted() {
            displayCurrentTime();
        }

        @Override
        public void onVideoEnded() {

        }

        @Override
        public void onError(YouTubePlayer.ErrorReason errorReason) {

        }
    };

    @Override
    public void onInitializationFailure(YouTubePlayer.Provider provider, YouTubeInitializationResult youTubeInitializationResult) {
        Toast.makeText(this, "Failed to initialize.", Toast.LENGTH_LONG).show();
    }

    SeekBar.OnSeekBarChangeListener mVideoSeekBarChangeListener = new SeekBar.OnSeekBarChangeListener() {
        @Override
        public void onProgressChanged(final SeekBar seekBar, final int progress, boolean fromUser) {
            long lengthPlayed = (mYoutubePlayer.getDurationMillis() * progress) / 100;
            mYoutubePlayer.seekToMillis((int) lengthPlayed);
            seekBar.setProgress(progress);
        }

        @Override
        public void onStartTrackingTouch(SeekBar seekBar) {

        }

        @Override
        public void onStopTrackingTouch(SeekBar seekBar) {

        }
    };

    private void displayCurrentTime() {
        if (mYoutubePlayer == null) return;
        String formattedTime = formatTime(mYoutubePlayer.getDurationMillis() - mYoutubePlayer.getCurrentTimeMillis());
        mPlayTimeTextView.setText(formattedTime);
    }

    private String formatTime(int millis) {
        int seconds = millis / 1000;
        int minutes = seconds / 60;
        int hours = minutes / 60;
        return (hours == 0 ? "- - : " : formatS(hours) + ":") + formatS(minutes % 60) + ":" + formatS(seconds % 60);
    }

    private String formatS(int x) {
        String s = "" + x;
        if (s.length() == 1)
            s = "0" + s;
        return s;
    }


    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            displayCurrentTime();

            mHandler.postDelayed(this, 100);
        }
    };

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.play_video:
                if (null != mYoutubePlayer && !mYoutubePlayer.isPlaying())
                    mYoutubePlayer.play();
                break;
            case R.id.pause_video:
                if (null != mYoutubePlayer && mYoutubePlayer.isPlaying())
                    mYoutubePlayer.pause();
                break;
        }
    }

    @Override
    protected void onPause() {
        super.onPause();
        mYoutubePlayer.release();
        mYoutubePlayer = null;
        mHandler.removeCallbacks(runnable);
    }
}


edit(2017/03/27)
上面無法和seekbar連動,當你移動seekbar影片會跟著移動,但影片再撥放的時候seekbar無法連動。
我試過RunOnUiThread設定seekbar會發生錯誤。
我修好的話會在上傳新code







third-party元件(Youtube API)─ Youtube API 簡易使用、匯入專案lib

前言:


此篇我們來實作內崁Youtube影音的功能。

為什麼不用WebView直接內崁?

因為WebView內崁的方式很醜阿。
會變下圖這樣


我不想要下面這些東西,想要單純的只有影片,其他東西我可以自由地運用。

當然還有一些比較特別的使用方式。

像是MixerBox,就是拿這個在營利。







1、首先先去這網頁下載他們的api
https://developers.google.com/youtube/android/player/downloads/


下載下來會是一個.zip檔案那要怎麼import到android studio內呢?



2、先解壓縮吧。
然後打開資料夾找到libs檔案我們需要的東西在裡面。

你會找到YouTubeAndroidPlayerApi.jar這的檔案。

然後找到你的專案在最最上層開一個libs資料夾,以後好方便管理你import的東西。
3、回到Android Studio內,右上方File >> Project Structure...


按下去之後,找到你自己專案,案ok


我們就可以使用Youtube API囉




程式碼:

先給網路權限吧!

AndroidManifest

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


1、增加YouTubePlayerView到xml內

在main_activity.java內設定View

main_activity.java

<?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.chat.a015865.youtube.MainActivity">

    <com.google.android.youtube.player.YouTubePlayerView
        android:id="@+id/player_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</android.support.constraint.ConstraintLayout>



2、初始化YouTubePlayerView(Initialize)

MainActivity.java內,在要使用YouTubePlayerViewactivity都一定要改extends繼承變成YouTubeBaseActivity,並且imp;ements YouTubePlayer.OnInitializedListener

implements YouTubePlayer.OnInitializedListener 之後,請implements methods

拿到onInitializationSuccess(初始化成功)和onInitializationFailure(初始化失敗)

在onCreate內初始化YouTubePlayerView

mYoutubePlayerView.initialize(API_KEY, this);

API_KEY目前用不到,可以先隨便給值。

Youtube網址的格式 https://www.youtube.com/watch?v= xxxxxx 後面的的xxxxxx可以改變你想看的頻道。試著去YOUTUBE上找你喜愛的頻道,COPY後面的xxxxxx回到MainActivity.java改變VEDIO_ID,就可以改變你要看的東西。


MainActivity.java

public class MainActivity extends YouTubeBaseActivity implements YouTubePlayer.OnInitializedListener {


    public static final String API_KEY = "htyhry";

    //https://www.youtube.com/watch?v=
    public static final String VIDEO_ID = "OsUr8N7t4zc";
    private YouTubePlayerView mYoutubePlayerView;

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

        mYoutubePlayerView = (YouTubePlayerView) findViewById(R.id.player_view);
        mYoutubePlayerView.initialize(API_KEY, this);
    }

    @Override
    public void onInitializationSuccess(YouTubePlayer.Provider provider, YouTubePlayer youTubePlayer, boolean wasRestored) {
        Toast.makeText(this, "onInitializationSuccess!", Toast.LENGTH_SHORT).show();
        if (youTubePlayer == null) {
            Log.d("CheckPoint", "CheckPoint youtubePlayer == null");
            return;
        }
        
        if (!wasRestored) {
            Log.d("CheckPoint", "CheckPoint !wasRestored");
            youTubePlayer.cueVideo(VIDEO_ID);
        }
    }

    @Override
    public void onInitializationFailure(YouTubePlayer.Provider provider, YouTubeInitializationResult youTubeInitializationResult) {
        Toast.makeText(this, "Failed to initialize.", Toast.LENGTH_LONG).show();

    }
}


這時候你就可以開始跑你的專案了,五月天的頑固就出現拉!


3、控制 YouTubePlayer 事件

其中還有一些控制YouTubePlayer的方法,像是可以再按暫停的同時,加入你要的廣告....之類的。
MainAcitivity.java內 >> onInitializationSuccess這裡面加入這個方法,可以控制Loading、onLoaded、onAdStarted....等等。

 youTubePlayer.setPlayerStateChangeListener(new YouTubePlayer.PlayerStateChangeListener() {
            @Override
            public void onLoading() {
                Log.d("CheckPoint", "CheckPoint onLoading");
            }

            @Override
            public void onLoaded(String s) {
                Log.d("CheckPoint", "CheckPoint onLoaded");
            }

            @Override
            public void onAdStarted() {
                Log.d("CheckPoint", "CheckPoint onAdStarted");
            }

            @Override
            public void onVideoStarted() {
                Log.d("CheckPoint", "CheckPoint onVideoStarted");
            }

            @Override
            public void onVideoEnded() {
                Log.d("CheckPoint", "CheckPoint onVideoEnded");
            }

            @Override
            public void onError(YouTubePlayer.ErrorReason errorReason) {
                Log.d("CheckPoint", "CheckPoint onError = " + errorReason);
            }
        });





文獻:


Demo:











2017年3月9日 星期四

third-party元件 ─ 網路下載圖片比較和簡易使用Fresco、Picasso 、Glide、ImageLoader

前言:

這幾天遇到OOM問題。
情況大概就是RecyclerView裡面跑大量的ImageView,導致崩潰。
RecyclerView我是寫在Fragment上面的,一開始我以為是因為Fragment沒有做好回收導致重複創建。

後來找到的問題,原來是網路下載圖片(ImageLoader)的方式緩存沒有做到釋放。
我更換了一下我讀取網路上圖片的的第三方,使用了Picasso發現問題就迎刃而解了!!

問了一下大家才發現如果對處理執行緒(Thread)不熟的話,不太推使用ImageLoader,它有一堆Leak的問題。
迷之聲:TMD緩存方式寫得天花亂墜的還不是一堆Leak


Android是比較推薦使用GlidePicasso



GlidePicasso
使用起來其實大同小異。
在這邊大致上介紹一下使用方式:


重點程式碼:
build.gradle(Module:app)

Picasso
免不了的import
compile 'com.squareup.picasso:picasso:2.5.2'

使用方式:
Picasso.with(context)
.load("http://i.imgur.com/DvpvklR.png")
.into(imageView);


Glide
免不了的import
compile 'com.github.bumptech.glide:glide:3.7.0'

使用方式:
Glide.with(context)
        .load(url)
        .into(imageView);


兩個第三方很像吧,是不是都簡單的不要不要的!


Fresco
Frescofacebook自己釋出的圖片下載第三方,你自己一定體驗過facebook它們寫的app雖然用到大量的圖片,但卻不會有卡頓、Crash掉的問題,你就知道Fresco有多麼的強大了!

推薦使用:
Glide > Picasso > Fresco > ImageLoader
當然這是比較主觀的推薦,也須依你當下的需求來使用。
如果圖片量不大,個人覺得Picasso是首選,因為他比較瘦。
當你的App內有大量的圖片需要處理,首選就會是Fresco


我擷取它人一段文章讓你參考:

Picasso所能實現的功能Glide都能做到,只是所需設置不同。兩者的區別是PicassoGlide體積小很多且圖像質量比Glide高,但Glide的速度比Picasso更快,Glide的長處是處理大型的圖片流,如gifvideo,如果要製作視頻類應用,Glide當為首選。
Fresco
可以說是綜合了之前圖片加載庫的優點,其在5.0以下的內存優化非常好,但它的不足是體積太大,按體積進行比較:Fresco>Glide>Picasso,所以Fresco在圖片較多的應用中更能凸顯其價值,如果應用沒有太多圖片需求,不推薦使用Fresco


文獻:



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

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

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