1. 程式人生 > >RecyclerView實現圖文混排

RecyclerView實現圖文混排

RecyclerView實現圖文混排

一、實現效果

RecyclerView實現圖文混排

WhatsNote專案地址:https://github.com/jicanghai37927/WhatsAndroid

二、設計思路

  • 實現圖片混排的幾種方案
  1. TextView通過ImageSpan新增圖片;
  2. WebView載入網頁方式;
  3. ScrollView
    RecyclerView等佈局控制元件組合TextViewImageView

WhatsNote採用的RecyclerView組合TextViewImageView的方式,這種方式的限制是文字與圖片只能按照固定的並排方式出現,無法實現圖片環繞等效果。圖片環繞需要通過TextViewSpan來實現。

  • 圖文混排實現過程
  1. Intent.ACTION_PICK選擇圖片;
  2. Glide載入圖片;
  3. ImageView顯示圖片;

三個過程完成新增圖片功能。

三、實現過程

1. 選擇圖片

圖片選擇採用Intent呼叫第三方Activity的方式實現,並且允許選擇多張圖片。

boolean requestPhoto() {
    SoftInputUtils.hide(getActivity());

    Intent intent = new Intent(Intent.ACTION_PICK, null);
    intent.setDataAndType(
        MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
        "image/*");
    intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);

    try {
        this
.startActivityForResult(intent, REQUEST_PHOTO); return true; } catch (Exception e) { } return false; }

onActivityResult中處理返回結果。

if ((resultCode == RESULT_OK) && (data != null)) {

    ArrayList<Uri> list = new ArrayList<>();

    {
        ClipData clipData = data.getClipData();
        if (clipData != null) {
            for (int i = 0; i < clipData.getItemCount(); i++) {
                ClipData.Item item = clipData.getItemAt(i);
                Uri uri = item.getUri();
                list.add(uri);
            }
        }

        if (list.isEmpty()) {
            Uri uri = data.getData();
            if (uri != null) {
                list.add(uri);
            }
        }
    }

    this.insertPhotos(list);

}

2. 載入圖片

圖片載入使用Glide實現,整個過程非常簡單。withloadapplyinto4個方法完成載入過程。

RequestOptions options = createRequestOptions(item);
Glide.with(parent)
    .load(item.getUri())
    .apply(options)
    .into(pictureView);
RequestOptions createRequestOptions(PictureEntity entity) {
    RequestOptions options = new RequestOptions();

    int maxWidth = this.getMaxWidth();
    int maxHeight = 3 * this.getMaxHeight();

    int width = maxWidth;
    int height = width * entity.getHeight() / entity.getWidth();
    if (height > maxHeight) {
        height = maxHeight;
        width = height * entity.getWidth() / entity.getHeight();

        width = (width > maxWidth)? maxWidth: width;
    }

    options.override(width, height);
    options.downsample(DownsampleStrategy.FIT_CENTER);
    options.signature(new ObjectKey(entity.getSignature()));

    return options;
}

重要的apply方法,指定了三個引數。

  • override圖片大小(寬度不大於螢幕寬度,高度不大於3倍螢幕高度,降低OutOfMemory出現概率)
  • downsample取樣模式(採用FIT_CENTER方式,居中縮放)
  • signature簽名

3. 顯示圖片

使用TargetSizeImageView顯示圖片,TargetSizeImageViewImageView的子類,通過指定目標尺寸決定控制元件大小。

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    if (targetWidth <= 0 || targetHeight <= 0) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        return;
    }

    int measureWidth = MeasureSpec.getSize(widthMeasureSpec);
    int measureHeight = MeasureSpec.getSize(heightMeasureSpec);

    if (measureWidth <= 0) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        return;
    }

    Log.v(TAG, "onMeasure width = " + measureWidth + ", height = " + measureHeight);

    int minWidth = this.getSuggestedMinimumWidth();
    int minHeight = this.getSuggestedMinimumHeight();

    int width = targetWidth;
    width = (width > measureWidth)? measureWidth : width; // width must not large then measureWidth
    width = (width < minWidth)? minWidth: width; // and not smaller then minWidth

    int height = width * targetHeight / targetWidth; // calculate height from width
    if (height < minHeight) { // recalculate width
        height = minHeight;
        width = height * targetWidth / targetHeight;

        width = (width > targetWidth)? targetWidth: width;
        width = (width > measureWidth)? measureWidth : width; // width must not large then measureWidth
        width = (width < minWidth)? minWidth: width; // and not smaller then minWidth
    }

    Log.v(TAG, "width = " + width + ", height = " + height);
    this.setMeasuredDimension(width, height);
}

TargetSizeImageView控制元件大小計算過程

  • targetWidth(指定的目標寬度)
  • measureWidth(傳入的測量寬度)
  • minWidth(最小寬度)保證小圖的顯示尺寸
int width = targetWidth; // 等於targetWidth
width = (width > measureWidth)? measureWidth : width; // 但不能大於measureWidth
width = (width < minWidth)? minWidth: width; // 並且不能小於minWidth

寬度確定後,根據targetWidthtargetHeight的比例計算高度。

int height = width * targetHeight / targetWidth; // 按比例計算高度

同時保證高度不小於最小高度。

if (height < minHeight) { // recalculate width
    height = minHeight;
    width = height * targetWidth / targetHeight;

    width = (width > targetWidth)? targetWidth: width;
    width = (width > measureWidth)? measureWidth : width; 
    width = (width < minWidth)? minWidth: width; 
}

四、最後

到這裡,我們實現了CRUD的Create和Read兩個環節。

Update和Delete將在後續過程中實現。