1. 程式人生 > >Android載入使用者頭像的功能實現

Android載入使用者頭像的功能實現

載入使用者頭像的過程是,首先從本地檢視是否儲存了使用者的頭像,如果有,則從本地讀取後加載到ImageView中,如果沒有,則去伺服器下載頭像儲存到本地,並載入。

public void initData() {
        super.initData();
        SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(MyApplication.getContext());
        final String u_id = pref.getString("u_id", "");
        // 聯網請求個人中心的資料
        getDataFromServer(u_id);
    }

在Fragment的onCreateView()中呼叫initData()方法,該方法使用使用者的id去聯網請求個人中心的資料。

private void getDataFromServer(String u_id) {
        String address = Constant.PERSONAL_CENTER_URL;
        HttpUtil.sendPostRequest(address, u_id, new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {

            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                if (null == response.body()) {
                    return;
                }

                String responseText = URLDecoder.decode(response.body().string(), "utf-8");
                // 解析資料
                processData(responseText);
            }
        });
    }

在 getDataFromServer()方法中呼叫了自己寫的HttpUtil工具類中的方法去請求伺服器資料。這個HttpUtil的方法如下:

public static void sendPostRequest(String address, String json, okhttp3.Callback callback) {
        OkHttpClient client = new OkHttpClient();
        RequestBody requestBody = RequestBody.create(JSON, json);
        Request request = new Request.Builder().url(address).post(requestBody).build();
        client.newCall(request).enqueue(callback);
    }

下面進入解析資料的部分,取出客戶端返回的資料,用Gson轉換為PersonMsg物件後取出User,用這個User中的資料來更新介面。從User中取出使用者id,利用id去sd卡中獲取到壓縮後的Bitmap點陣圖(這裡是個大坑,不壓縮很容易造成記憶體溢位OOM的錯誤,填坑請看我另一篇博文:解決使用Bitmap造成OOM記憶體溢位的問題)。如果取出的Bitmap不為空,則表示從本地載入到了使用者頭像,否則就從伺服器請求頭像資料。

    private void processData(String response) {
        final PersonMsg personMsg = Utility.handlePersonMsgResponse(response);
        if (null == personMsg) {
            return;
        }

        final User user = personMsg.getUser();

        // 更新介面
        if (getActivity() == null) {
            return;
        }

        final Bitmap bitmap = ImageUtils.getPhotoFromStorage(user.getU_id());
        if (bitmap != null) {
            getActivity().runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    Log.i("載入使用者頭像", "本地載入頭像");
                    Glide.with(getActivity()).load(bitmap).into(mIvPhoto);
                }
            });
        } else {
            // 從伺服器請求頭像資料
            asyncGet(Constant.BASE_PHOTO_URL + user.getU_photo());
        }

        getActivity().runOnUiThread(new Runnable() {
            @Override
            public void run() {
                mTvNick.setText(user.getU_nick());
                mTvGain.setText(String.valueOf(user.getU_gain()));
                boolean male = user.getU_sex() == 1;
                if (male) {
                    Glide.with(getActivity()).load(R.drawable.ic_male).into(mIvSex);
                } else {
                    Glide.with(getActivity()).load(R.drawable.ic_female).into(mIvSex);
                }
            }
        });

        // 儲存使用者資料到本地資料庫
        saveToDatabase(personMsg);
    }

附上用到的ImageUtils.getPhotoFromStorage()的程式碼部分,這部分程式碼就是從sd卡中取出圖片並壓縮的過程:

	public static Bitmap getPhotoFromStorage(String u_id) {
        String photoPath = android.os.Environment.getExternalStorageDirectory() + "/photo/" + u_id + ".jpg";
        return getBitmapFromPath(photoPath, 80, 80);
    }
    // 從路徑獲取Bitmap
    public static Bitmap getBitmapFromPath(String imgPath, int reqWidth, int reqHeight) {
        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(imgPath, options);

        // Calculate inSampleSize
        options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

        // Decode bitmap with inSampleSize set
        options.inJustDecodeBounds = false;
        //避免出現記憶體溢位的情況,進行相應的屬性設定。
        options.inPreferredConfig = Bitmap.Config.RGB_565;
        options.inDither = true;

        return BitmapFactory.decodeFile(imgPath, options);
    }
    
	public static int calculateInSampleSize( //參2和3為ImageView期待的圖片大小
                                             BitmapFactory.Options options, int reqWidth, int reqHeight) {
        // 圖片的實際大小
        final int height = options.outHeight;
        final int width = options.outWidth;
        //預設值
        int inSampleSize = 1;
        //動態計算inSampleSize的值
        if (height > reqHeight || width > reqWidth) {
            final int halfHeight = height/2;
            final int halfWidth = width/2;
            while( (halfHeight/inSampleSize) >= reqHeight && (halfWidth/inSampleSize) >= reqWidth){
                inSampleSize *= 2;
            }
        }
        return inSampleSize;
    }

從伺服器獲取圖片的程式碼如下:

	private void asyncGet(String imgUrl) {
        OkHttpClient client = new OkHttpClient();
        final Request request = new Request.Builder().get()
                .url(imgUrl)
                .build();

        client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                e.printStackTrace();
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                if (response.isSuccessful()) {
                    if (response.body() != null) {
                        byte[] bytes = response.body().bytes();
                        final Bitmap bitmap = ImageUtils.getBitmapFromByte(bytes, 70, 70);
                        ImageUtils.savePhotoToStorage(bitmap, pref.getString("u_id", ""));
                        if (getActivity() != null) {
                            getActivity().runOnUiThread(new Runnable() {
                                @Override
                                public void run() {
                                    Glide.with(getActivity()).load(bitmap).into(mIvPhoto);
                                }
                            });
                            Log.i("載入使用者頭像", "從伺服器載入頭像");
                        }
                    }
                }
            }
        });
    }

伺服器返回的是圖片的位元組陣列,我將其轉換為壓縮後的Bitmap,並將其儲存到sd卡,下次讀取時就可直接從sd卡中獲取。

	// 儲存圖片到sd卡
    public static void savePhotoToStorage(Bitmap photoBitmap, String u_id) {
        //更改的名字
        String photoName = u_id + ".jpg";
        String photoPath = android.os.Environment.getExternalStorageDirectory() +
                "/photo";

        File fileDir = new File(photoPath);
        if(!fileDir.exists()){
            fileDir.mkdirs();
        }
        FileOutputStream fos = null;
        File photoFile = null;
        try{
            //重新命名並儲存
            photoFile = new File(photoPath, photoName);
            photoFile.createNewFile();

            fos = new FileOutputStream(photoFile);
            photoBitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos);
            fos.flush();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if(fos != null){
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
	public static Bitmap getBitmapFromByte(byte[] imgByte, int reqWidth, int reqHeight) {
        InputStream input = null;
        Bitmap bitmap = null;
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);;
        input = new ByteArrayInputStream(imgByte);
        SoftReference softRef = new SoftReference(BitmapFactory.decodeStream(
                input, null, options));
        bitmap = (Bitmap) softRef.get();
        if (imgByte != null) {
            imgByte = null;
        }

        try {
            if (input != null) {
                input.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return bitmap;
    }

以上整個程式碼中融入了我自己專案處理的一些邏輯,實際使用中還要根據自己專案的需要來適當更改。