1. ホーム
  2. アンドロイド

AndroidでImageViewに直接Web画像を表示する

2022-02-24 08:46:59
<ブロッククオート

オリジナルのブログ記事です。 土井テクニカルチーム
リンクアドレスです。 https://blog.doiduoyi.com/authors/1584446358138
はじまり。偉大な土井技術チームの学習経験を記録する

       ネイティブのImageViewでは、Web上の画像を直接表示するメソッドはありません。Webの画像を表示する必要がよくある場合、その都度操作が必要で面倒なので、今日はImageViewで簡単にWebの画像を表示する方法をお教えします。

ImageViewメソッドのカスタマイズ

ImageViewを継承したクラスを作成し、setImageURL(path)メソッドを追加してください。

import android.content;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;
import android.os;
import android.util;
import android.widget.ImageView;
import android.widget.Toast;

import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;

public class MyImageView extends ImageView {
    public static final int GET_DATA_SUCCESS = 1;
    public static final int NETWORK_ERROR = 2;
    public static final int SERVER_ERROR = 3;
    // sub-threads can not operate the UI, set the image through Handler
    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
           switch (msg.what){
               case GET_DATA_SUCCESS:
                   Bitmap bitmap = (Bitmap) msg.obj;
                   setImageBitmap(bitmap);
                   break;
               case NETWORK_ERROR:
                   Toast.makeText(getContext(),"Network connection failed",Toast.LENGTH_SHORT).show();
                   break;
               case SERVER_ERROR:
                   Toast.makeText(getContext(),"Server error occurred",Toast.LENGTH_SHORT).show();
                   break;
           }
        }
    };

    public MyImageView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    public MyImageView(Context context) {
        super(context);
    }

    public MyImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    // Set the network image
    public void setImageURL(final String path) {
        //open a thread for networking
        new Thread() {
            @Override
            public void run() {
                try {
                    //transform the passed path to a URL
                    URL url = new URL(path);
                    //Get the connection
                    HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                    //use the GET method to access the network
                    connection.setRequestMethod("GET");
                    //timeout is 10 seconds
                    connection.setConnectTimeout(10000);
                    //Get the return code
                    int code = connection.getResponseCode();
                    if (code == 200) {
                        InputStream inputStream = connection.getInputStream();
                        // Use the factory to produce a Bitmap from the network's input stream
                        Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
                        //Use Message to send the image to Handler
                        Message msg = Message.obtain();
                        msg.obj = bitmap;
                        msg.what = GET_DATA_SUCCESS;
                        handler.sendMessage(msg);
               inputStream.close();
                    }else {
                        //Error occurred on service start
                        handler.sendEmptyMessage(SERVER_ERROR);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                    // Network connection error
                    handler.sendEmptyMessage(NETWORK_ERROR);
                }
            }
        }.start();
    }

}

レイアウト上でImageViewを使用することはできません。MyImageViewを使用するには、先ほど書き換えたMyImageViewの1つへのフルパスを記述してください。

<Button
        android:text="Load web image"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/button" />

    <com.example.dell.myapplication.MyImageView
        android:id="@+id/image_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

MainActivity上で、setImageURL()を呼び出し、Web画像へのパスを直接書き込むだけで、Web画像が表示されます。

final MyImageView myImageView = (MyImageView) findViewById(R.id.image_view);
        Button button = (Button) findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
          // write the path of the image directly to the network to display the image of the network
                myImageView.setImageURL("https://pic.cnblogs.com/avatar/1142647/20170416093225.png");
            }
        });


最後に、ネットワークへのアクセスを追加することを忘れないでください。

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

レンダリング

圧縮率

       Webから写真を取得してImageViewに直接表示するのは比較的簡単ですが、Web画像が大きくスマホの画面サイズを超えてしまった場合、元の画像を読み込んだままだとメモリの無駄遣いやメモリオーバーの可能性があるので、Web画像を圧縮する必要があることを考えたことがありますか?

まず表示するImageViewの幅と高さを取得する

    /**
     * Get the actual width of the ImageView
     * @return Returns the actual width of the ImageView
     */
    public int realImageViewWith() {
        DisplayMetrics displayMetrics = getContext().getResources().getDisplayMetrics();
        ViewGroup.LayoutParams layoutParams = getLayoutParams();

        // If the ImageView sets the width, you can get the real width
        int width = getWidth();
        if (width <= 0) {
            //If the ImageView does not set a width, get the width of the parent container
            width = layoutParams.width;
        }
        if (width <= 0) {
            //Get the maximum width of the ImageView
            width = getMaxWidth();
        }
        if (width <= 0) {
            //Get the width of the screen
            width = displayMetrics.widthPixels;
        }
        Log.e("Actual width of ImageView", String.valueOf(width));
        return width;
    }

    /**
     * Get the actual height of the ImageView
     * @return Return the actual height of the ImageView
     */
    public int realImageViewHeight() {
        DisplayMetrics displayMetrics = getContext().getResources().getDisplayMetrics();
        ViewGroup.LayoutParams layoutParams = getLayoutParams();

        //If the ImageView has a height set, you can get the real width
        int height = getHeight();
        if (height <= 0) {
            //If the ImageView doesn't have a height set, get the height of the parent container
            height = layoutParams.height;
        }
        if (height <= 0) {
            //Get the maximum height of the ImageView
            height = getMaxHeight();
        }
        if (height <= 0) {
            //Get the maximum value of the ImageView height
            height = displayMetrics.heightPixels;
        }
        Log.e("Actual height of ImageView", String.valueOf(height));
        return height;
    }

次に、ウェブ画像のサイズから圧縮する比率を計算します。

    /**
     * Get the ratio to be compressed
     *
     * @param options needs to be passed in already BitmapFactory.decodeStream(is, null, options);
     * @return return the ratio of compression, minimum is 1
     */
    public int getInSampleSize(BitmapFactory.Options options) {
        int inSampleSize = 1;
        int realWith = realImageViewWith();
        int realHeight = realImageViewHeight();

        int outWidth = options.outWidth;
        Log.e("The actual width of the network image", String.valueOf(outWidth));
        int outHeight = options.outHeight;
        Log.e("The actual height of the network image", String.valueOf(outHeight));

        //Get the one with the largest ratio
        if (outWidth > realWith || outHeight > realHeight) {
            int withRadio = Math.round(outWidth / realWith);
            int heightRadio = Math.round(outHeight / realHeight);
            inSampleSize = withRadio > heightRadio ? withRadio : heightRadio;
        }
        Log.e("compression ratio", String.valueOf(inSampleSize));
        return inSampleSize;
    }

圧縮率を求めたら、圧縮の準備です

    /**
     * Return a compressed image based on the input stream
     * @param input input stream of the image
     * @return compressed image
     */
    public Bitmap getCompressBitmap(InputStream input) {
        // Because the InputStream has to be used twice, but using it once is invalid, so you need to copy two
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try {
            byte[] buffer = new byte[1024];
            int len;
            while ((len = input.read(buffer)) > -1 ) {
                baos.write(buffer, 0, len);
            }
            baos.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }

        // Copy the new input stream
        InputStream is = new ByteArrayInputStream(baos.toByteArray());
        InputStream is2 = new ByteArrayInputStream(baos.toByteArray());

        // just get the size of the network image, not really get the image
        BitmapFactory.Options options options = new BitmapFactory;
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeStream(is, null, options);
        //Get the image and compress it
        options.inSampleSize = getInSampleSize(options);
        options.inJustDecodeBounds = false;
        return BitmapFactory.decodeStream(is2, null, options);
    }

最後に、setImageURL()メソッド内で、Bitmap bitmap = BitmapFactory.decodeStream(inputStream); を次のように変更します。

Bitmap bitmap = getCompressBitmap(inputStream);

キャッシュ

時には運用効率を向上させ、トラフィックを保存するには、多くの場合、キャッシュ、データキャッシュを使用しない場合でも、ネットワークを使用することができます、次のキャッシュを学ぶために開始、私はこのようなキャッシュは、正式なキャッシュ技術ではありません。

setImageURL()メソッドを次のように変更し、2つのグローバル変数imagePath、isUseCacheを追加します。

    //Whether caching is enabled
    public boolean isUseCache = false;
   private String imagePath;

    // Set the network image
    public void setImageURL(String path) {
        imagePath = path;
        if (isUseCache){
            useCacheImage();
        }else {
            useNetWorkImage();
        }
    }

以前の setImageURL() の機能のほとんどを useNetWorkImage() メソッドに入れ、画像をキャッシュするかどうかという決定を追加しました。

    // Use the network image display
    public void useNetWorkImage(){
        //open a thread for networking
        new Thread() {
            @Override
            public void run() {
                try {
                    //transform the passed path to a URL
                    URL url = new URL(imagePath);
                    //Get the connection
                    HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                    //use the GET method to access the network
                    connection.setRequestMethod("GET");
                    //timeout is 10 seconds
                    connection.setConnectTimeout(10000);
                    //Get the return code
                    int code = connection.getResponseCode();
                    if (code == 200) {
                        Bitmap bitmap;
                        //Get the network input stream
                        InputStream inputStream = connection.getInputStream();

                        // Determine if the cached image is being used
                        if (isUseCache){
                            //because the InputStream has to be used twice, but using it once is invalid, so you need to copy two
                            ByteArrayOutputStream baos = new ByteArrayOutputStream();
                            try {
                                byte[] buffer = new byte[1024];
                                int len;
                                while ((len = inputStream.read(buffer)) > -1) {
                                    baos.write(buffer, 0, len);
                                }
                                baos.flush();
                            } catch (IOException e) {
                                e.printStackTrace();
                            }

                            // Copy the new input stream
                            InputStream is = new ByteArrayInputStream(baos.toByteArray());
                            InputStream is2 = new ByteArrayInputStream(baos.toByteArray());

                            // call the compression method to display the image
                             bitmap = getCompressBitmap(is);
                            //call the cache image method
                            cacheImage(is2);
                        }else {
                            //call the compress method
                             bitmap = getCompressBitmap(inputStream);
                        }

                        //Use Message to send the image to Handler
                        Message msg = Message.obtain();
                        msg.obj = bitmap;
                        msg.what = GET_DATA_SUCCESS;
                        handler.sendMessage(msg);
                        inputStream.close();
                    } else {
                        //service start error occurred
                        handler.sendEmptyMessage(SERVER_ERROR);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                    // Network connection error
                    handler.sendEmptyMessage(NETWORK_ERROR);
                }
            }
        }.start();
    }

 渡されたURLを元に一意のファイルを生成し、そのパスを元に画像を生成し、キャッシュされた画像があるかどうかを調べるメソッドを作成します。

    /**
     * Generate a filename based on the URL
     * @return filename
     */
    public String getURLPath() {
        StringBuilder urlStr2 = new StringBuilder();
        String[] strings = imagePath.split("\\\/");
        for (String string : strings) {
            urlStr2.append(string);
        }
        Log.e("MyImageView","Filename: "+urlStr2.toString());
        return urlStr2.toString();
    }

生成されたパスに基づいて画像をキャッシュする

    /**
     * Cache images for the network
     * @param inputStream The input stream for the network
     */
    public void cacheImage(InputStream inputStream) {
        try {
            File file = new File(getContext().getCacheDir(), getURLPath());
            FileOutputStream fos = new FileOutputStream(file);
            int len;
            byte[] buffer = new byte[1024];
            while ((len = inputStream.read(buffer)) ! = -1) {
                fos.write(buffer, 0, len);
            }
            fos.close();
            Log.e("MyImageView","Cache successful");
        } catch (IOException e) {
            e.printStackTrace();
            Log.e("MyImageView","Cache failed");
        }
    }


最後に、キャッシュされた画像を使用することができます。

    // Use cached images
    public void useCacheImage() {
        // Create a file with the same path
        File file = new File(getContext().getCacheDir(), getURLPath());
        // determine if the file exists
        if (file ! = null && file.length() > 0) {
            //use local image
            try {
                InputStream inputStream = new FileInputStream(file);
                // call the compression method to display the image
                Bitmap bitmap = getCompressBitmap(inputStream);
                //Use Message to send the image to Handler
                Message msg = Message.obtain();
                msg.obj = bitmap;
                msg.what = GET_DATA_SUCCESS;
                handler.sendMessage(msg);
                Log.e("MyImageView","Using cached image");
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
        }else {
            // Use a network image
            useNetWorkImage();
            Log.e("MyImageView","usingNetImage");
        }
    }

キャッシュを使用するためには、isUseCache を true に設定する必要があります。

        // Write the path to the web image directly to display the web image
          String url = "https://pic.cnblogs.com/avatar/1142647/20170416093225.png";
         //set to true to enable caching, default is false
          myImageView.isUseCache = true;
         myImageView.setImageURL(url); 

MyImageView.javaのコード全体

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;
import android.os;
import android.util;
DisplayMetrics;
DisplayMetrics; import android.util;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.Toast;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;

public class MyImageView extends ImageView {
    private String imagePath;
    // whether to enable caching
    public boolean isUseCache = false;

    public static final int GET_DATA_SUCCESS = 1;
    public static final int NETWORK_ERROR = 2;
    public static final int SERVER_ERROR = 3;
    // sub-threads can not operate the UI, set the image through Handler
    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case GET_DATA_SUCCESS:
                    Bitmap bitmap = (Bitmap) msg.obj;
                    setImageBitmap(bitmap);
                    break;
                case NETWORK_ERROR:
                    Toast.makeText(getContext(), "Network connection failed", Toast.LENGTH_SHORT).show();
                    break;
                case SERVER_ERROR:
                    Toast.makeText(getContext(), "An error occurred on the server", Toast.LENGTH_SHORT).show();
                    break;
            }
        }
    };

    public MyImageView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    public MyImageView(Context context) {
        super(context);
    }

    public MyImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    // Set the network image
    public void setImageURL(String path) {
        imagePath = path;
        if (isUseCache){
            useCacheImage();
        }else {
            useNetWorkImage();
        }
    }

    // use the network image display
    public void useNetWorkImage(){
        //open a thread for networking
        new Thread() {
            @Override

                    // Network connection error
                    handler.sendEmptyMessage(NETWORK_ERROR);
                }
            }
        }.start();
    }

    // Use cached image
    public void useCacheImage() {
        //create a file with the same path
        File file = new File(getContext().getCacheDir(), getURLPath());
        // determine if the file exists
        if (file ! = null && file.length() > 0) {
            //use local image
            try {
                InputStream inputStream = new FileInputStream(file);
                // call the compression method to display the image
                Bitmap bitmap = getCompressBitmap(inputStream);
                //Use Message to send the image to Handler
                Message msg = Message.obtain();
                msg.obj = bitmap;
                msg.what = GET_DATA_SUCCESS;
                handler.sendMessage(msg);
                Log.e("MyImageView","Using cached image");
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
        }else {
            // Use a network image
            useNetWorkImage();
            Log.e("MyImageView","usingNetImage");
        }
    }

    /**
     * Cache images from the network
     * @param inputStream The input stream for the network
     */
    public void cacheImage(InputStream inputStream) {
        try {
            File file = new File(getContext().getCacheDir(), getURLPath());
            FileOutputStream fos = new FileOutputStream(file);
            int len;
            byte[] buffer = new byte[1024];
            while ((len = inputStream.read(buffer)) ! = -1) {
                fos.write(buffer, 0, len);
            }
            fos.close();
            Log.e("MyImageView","Cache successful");
        } catch (IOException e) {
            e.printStackTrace();
            Log.e("MyImageView","Cache failed");
        }
    }

    /**
     * Generate a file name based on the URL
     * @return filename
     */
    public String getURLPath() {
        StringBuilder urlStr2 = new StringBuilder();
        String[] strings = imagePath.split("\\\/");
        for (String string : strings) {
            urlStr2.append(string);
        }
        Log.e("MyImageView","File name: "+urlStr2.toString());
        return urlStr2.toString();
    }


    /**
     * Returns a compressed image based on the input stream
     *
     * @param input The input stream of the image
     * @return compressed image
     */
    public Bitmap getCompressBitmap(InputStream input) {
        // Because the InputStream has to be used twice, but using it once is invalid, so you need to copy two
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try {
            byte[] buffer = new byte[1024];
            int len;
            while ((len = input.read(buffer)) > -1) {
                baos.write(buffer, 0, len);
            }
            baos.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }

        // Copy the new input stream
        InputStream is = new ByteArrayInputStream(baos.toByteArray());
        InputStream is2 = new ByteArrayInputStream(baos.toByteArray());

        // just get the size of the network image, not really get the image
        BitmapFactory.Options options options = new BitmapFactory;
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeStream(is, null, options);
        //Get the image and compress it
        options.inSampleSize = getInSampleSize(options);
        options.inJustDecodeBounds = false;
        return BitmapFactory.decodeStream(is2, null, options);
    }

    /**
     * Get the ratio to be compressed
     *
     * @param options needs to be passed in already BitmapFactory.decodeStream(is, null, options);
     * @return return the ratio of compression, minimum 1
     */
    public int getInSampleSize(BitmapFactory.Options options) {
        int inSampleSize = 1;
        int realWith = realImageViewWith();
        int

 Web画像を利用したエフェクト画像

キャッシュを利用した効果イメージ

画像読み込みフレームワーク「Glide」の利用

 この非常に高度なオープンソースの時代に、私たちがそのような複雑な作業をする必要がないように作られている様々なオープンソースのフレームワークがあるのは確かです。ここでは、画像読み込みフレームワークGlideの簡単な使い方を紹介します。

Glideを使用する前にGlideの依存関係を追加するには

compile 'com.github.bumptech.glide:glide:4.0.0'

先ほどと同じ条件で、クリックイベントのアクションを次の2行のコードに置き換えます。

String url = "https://pic.cnblogs.com/avatar/1142647/20170416093225.png";
Glide.with(MainActivity.this).load(url).into(myImageView);


このオープンソースのライブラリがあれば、まだあの膨大なコードの山を書きたいとは思わないでしょうし、さらに強力なのは、すでにキャッシュ機能を備えていて、あなたのためにすべてをやってくれることです。

Web画像の読み込みのイメージ図

キャッシュを使用した場合の効果イメージ

このように強力なオープンソースライブラリなので、その使い方を簡単に理解しましょう。まず、6つの引数を取ることができる with() メソッドのソースコードから、さまざまな状況で動作することを確認します。

    public static RequestManager with(Context context) {
        return getRetriever(context).get(context);
    }

    public static RequestManager with(Activity activity) {
        return getRetriever(activity).get(activity);
    }

    public static RequestManager with(FragmentActivity activity) { return getRetriever(activity).get(activity); }
        return getRetriever(activity).get(activity);
    }

    public static RequestManager with(android.app.Fragment fragment) { return getRetriever(activity).get(activity); }
        return getRetriever(fragment.getActivity()).get(fragment);
    }

    public static RequestManager with(Fragment fragment) {
        return getRetriever(fragment.getActivity()).get(fragment);
    }

    public static RequestManager with(View view) {
        return getRetriever(view.getContext()).get(view);
    }

例えば、String 型を渡すと、ネットワークから画像を読み込んでくれますが、File(ローカル画像を読み込む)、int(アプリケーションファイルのソースを読み込む)、byte[] (バイトストリーム)、Uri

public RequestBuilder<Drawable> load(@Nullable Object model) {
    return asDrawable().load(model);
  }

 とりあえずアプリの画像を読み込んでみよう

Glide.with(MainActivity.this).load(R.mipmap.ic_launcher).into(myImageView);

レンダリング

最後に、into()メソッドで、表示したいImageViewを読み込んで完了です。

繰り返しの処理 グライド-->with()-->load()-->into()

プロジェクトのソースコードです。 https://resource.doiduoyi.com/#2o4csq2