Android 仿朋友圈之九宮格多圖顯示(二)
阿新 • • 發佈:2018-12-19
一個仿微信朋友圈和QQ空間的九宮格圖片展示自定義控制元件效果:
一.介紹:
1、當只有1張圖時,可以自己定製圖片寬高,也可以使用預設九宮格的寬高; 2、當只有4張圖時,以2*2的方式顯示; 3、除以上兩種情況下,都是按照3列方式顯示,但這時有一些細節: a、如果只有9張圖,當然是以3*3的方式顯示; b、如果超過9張圖,可以設定是否全部顯示。 如果設定不完全顯示,則按照3*3的方式顯示,但是在第9張圖上會有一個帶“+”號的數字, 代表還有幾張沒有顯示,這裡是模仿了QQ空間圖片超出9張的顯示方式; 如果設定全部顯示,理所當然的將所有圖片都顯示出來。 4、圖片被按下時,會有一個變暗的效果,這也是模仿微信朋友圈的效果。
二.使用步驟:
1、核心類是NineGridLayout,繼承自ViewGroup的抽象類,所以我們實際專案使用需要繼承它,並要實現3個方法,如下:
import android.content.Context; import android.content.res.TypedArray; import android.graphics.Color; import android.graphics.Paint; import android.util.AttributeSet; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; import com.example.donghe.wxfriend.R; import java.util.ArrayList; import java.util.List; import java.util.TimerTask; public abstract class NineGridLayout extends ViewGroup { private static final float DEFUALT_SPACING = 3f; private static final int MAX_COUNT = 9; protected Context mContext; private float mSpacing = DEFUALT_SPACING; private int mColumns; private int mRows; private int mTotalWidth; private int mSingleWidth; private boolean mIsShowAll = false; private boolean mIsFirst = true; private ArrayList<String> mUrlList = new ArrayList<>(); public NineGridLayout(Context context) { super(context); init(context); } public NineGridLayout(Context context, AttributeSet attrs) { super(context, attrs); TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.NineGridLayout); mSpacing = typedArray.getDimension(R.styleable.NineGridLayout_sapcing, DEFUALT_SPACING); typedArray.recycle(); init(context); } private void init(Context context) { mContext = context; if (getListSize(mUrlList) == 0) { setVisibility(GONE); } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { mTotalWidth = right - left; mSingleWidth = (int) ((mTotalWidth - mSpacing * (3 - 1)) / 3); if (mIsFirst) { notifyDataSetChanged(); mIsFirst = false; } } /** * 設定間隔 * * @param spacing */ public void setSpacing(float spacing) { mSpacing = spacing; } /** * 設定是否顯示所有圖片(超過最大數時) * * @param isShowAll */ public void setIsShowAll(boolean isShowAll) { mIsShowAll = isShowAll; } public void setUrlList(List<String> urlList) { if (getListSize(urlList) == 0) { setVisibility(GONE); return; } setVisibility(VISIBLE); mUrlList.clear(); mUrlList.addAll(urlList); if (!mIsFirst) { notifyDataSetChanged(); } } public void notifyDataSetChanged() { post(new TimerTask() { @Override public void run() { refresh(); } }); } private void refresh() { removeAllViews(); int size = getListSize(mUrlList); if (size > 0) { setVisibility(VISIBLE); } else { setVisibility(GONE); } if (size == 1) { String url = mUrlList.get(0); RatioImageView imageView = createImageView(0, url); //避免在ListView中一張圖未載入成功時,佈局高度受其他item影響 LayoutParams params = getLayoutParams(); params.height = mSingleWidth; setLayoutParams(params); imageView.layout(0, 0, mSingleWidth, mSingleWidth); boolean isShowDefualt = displayOneImage(imageView, url, mTotalWidth); if (isShowDefualt) { layoutImageView(imageView, 0, url, false); } else { addView(imageView); } return; } generateChildrenLayout(size); layoutParams(); for (int i = 0; i < size; i++) { String url = mUrlList.get(i); RatioImageView imageView; if (!mIsShowAll) { if (i < MAX_COUNT - 1) { imageView = createImageView(i, url); layoutImageView(imageView, i, url, false); } else { //第9張時 if (size <= MAX_COUNT) {//剛好第9張 imageView = createImageView(i, url); layoutImageView(imageView, i, url, false); } else {//超過9張 imageView = createImageView(i, url); layoutImageView(imageView, i, url, true); break; } } } else { imageView = createImageView(i, url); layoutImageView(imageView, i, url, false); } } } private void layoutParams() { int singleHeight = mSingleWidth; //根據子view數量確定高度 LayoutParams params = getLayoutParams(); params.height = (int) (singleHeight * mRows + mSpacing * (mRows - 1)); setLayoutParams(params); } private RatioImageView createImageView(final int i, final String url) { RatioImageView imageView = new RatioImageView(mContext); imageView.setScaleType(ImageView.ScaleType.CENTER_CROP); imageView.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { onClickImage(i, url, mUrlList); } }); return imageView; } /** * @param imageView * @param url * @param showNumFlag 是否在最大值的圖片上顯示還有未顯示的圖片張數 */ private void layoutImageView(RatioImageView imageView, int i, String url, boolean showNumFlag) { final int singleWidth = (int) ((mTotalWidth - mSpacing * (3 - 1)) / 3); int singleHeight = singleWidth; int[] position = findPosition(i); int left = (int) ((singleWidth + mSpacing) * position[1]); int top = (int) ((singleHeight + mSpacing) * position[0]); int right = left + singleWidth; int bottom = top + singleHeight; imageView.layout(left, top, right, bottom); addView(imageView); if (showNumFlag) {//新增超過最大顯示數量的文字 int overCount = getListSize(mUrlList) - MAX_COUNT; if (overCount > 0) { float textSize = 30; final TextView textView = new TextView(mContext); textView.setText("+" + String.valueOf(overCount)); textView.setTextColor(Color.WHITE); textView.setPadding(0, singleHeight / 2 - getFontHeight(textSize), 0, 0); textView.setTextSize(textSize); textView.setGravity(Gravity.CENTER); textView.setBackgroundColor(Color.BLACK); textView.getBackground().setAlpha(120); textView.layout(left, top, right, bottom); addView(textView); } } displayImage(imageView, url); } private int[] findPosition(int childNum) { int[] position = new int[2]; for (int i = 0; i < mRows; i++) { for (int j = 0; j < mColumns; j++) { if ((i * mColumns + j) == childNum) { position[0] = i;//行 position[1] = j;//列 break; } } } return position; } /** * 根據圖片個數確定行列數量 * * @param length */ private void generateChildrenLayout(int length) { if (length <= 3) { mRows = 1; mColumns = length; } else if (length <= 6) { mRows = 2; mColumns = 3; if (length == 4) { mColumns = 2; } } else { mColumns = 3; if (mIsShowAll) { mRows = length / 3; int b = length % 3; if (b > 0) { mRows++; } } else { mRows = 3; } } } protected void setOneImageLayoutParams(RatioImageView imageView, int width, int height) { imageView.setLayoutParams(new LayoutParams(width, height)); imageView.layout(0, 0, width, height); LayoutParams params = getLayoutParams(); // params.width = width; params.height = height; setLayoutParams(params); } private int getListSize(List<String> list) { if (list == null || list.size() == 0) { return 0; } return list.size(); } private int getFontHeight(float fontSize) { Paint paint = new Paint(); paint.setTextSize(fontSize); Paint.FontMetrics fm = paint.getFontMetrics(); return (int) Math.ceil(fm.descent - fm.ascent); } /** * @param imageView * @param url * @param parentWidth 父控制元件寬度 * @return true 代表按照九宮格預設大小顯示,false 代表按照自定義寬高顯示 */ protected abstract boolean displayOneImage(RatioImageView imageView, String url, int parentWidth); protected abstract void displayImage(RatioImageView imageView, String url); protected abstract void onClickImage(int position, String url, ArrayList<String> urlList); }
2、我這裡用NineGridTestLayout繼承NineGridLayout實現,displayOneImage()與displayImage()中的引數都是顯示圖片需要的,我這裡用的是ImageLoader顯示圖片,當然你也可以用其他的
import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; import android.os.Bundle; import android.util.AttributeSet; import android.view.View; import android.widget.Toast; import com.example.donghe.wxfriend.activity.GalleryActivity; import com.example.donghe.wxfriend.activity.MyGalleryActivity; import com.nostra13.universalimageloader.core.assist.FailReason; import com.nostra13.universalimageloader.core.listener.ImageLoadingListener; import java.util.ArrayList; import java.util.List; public class NineGridTestLayout extends NineGridLayout { protected static final int MAX_W_H_RATIO = 3; public NineGridTestLayout(Context context) { super(context); } public NineGridTestLayout(Context context, AttributeSet attrs) { super(context, attrs); } @Override protected boolean displayOneImage(final RatioImageView imageView, String url, final int parentWidth) { ImageLoaderUtil.displayImage(mContext, imageView, url, ImageLoaderUtil.getPhotoImageOption(), new ImageLoadingListener() { @Override public void onLoadingStarted(String imageUri, View view) { } @Override public void onLoadingFailed(String imageUri, View view, FailReason failReason) { } @Override public void onLoadingComplete(String imageUri, View view, Bitmap bitmap) { int w = bitmap.getWidth(); int h = bitmap.getHeight(); int newW; int newH; if (h > w * MAX_W_H_RATIO) {//h:w = 5:3 newW = parentWidth / 2; newH = newW * 5 / 3; } else if (h < w) {//h:w = 2:3 newW = parentWidth * 2 / 3; newH = newW * 2 / 3; } else {//newH:h = newW :w newW = parentWidth / 2; newH = h * newW / w; } setOneImageLayoutParams(imageView, newW, newH); } @Override public void onLoadingCancelled(String imageUri, View view) { } }); return false; } @Override protected void displayImage(RatioImageView imageView, String url) { ImageLoaderUtil.getImageLoader(mContext).displayImage(url, imageView, ImageLoaderUtil.getPhotoImageOption()); } @Override protected void onClickImage(int i, String url, ArrayList<String> urlList) { Toast.makeText(mContext, "點選了圖片" + url, Toast.LENGTH_SHORT).show(); Intent intent = new Intent(mContext, MyGalleryActivity.class); Bundle bundle = new Bundle(); bundle.putString("999",url); bundle.putStringArrayList("list",urlList); bundle.putInt("po",i); intent.putExtras(bundle); mContext.startActivity(intent); } }
3.佈局檔案:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="8dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:id="@+id/iv_head"
android:layout_width="50dp"
android:layout_height="50dp" />
<TextView
android:id="@+id/tv_username"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginLeft="4dp"
android:text="asas" />
</LinearLayout>
<TextView
android:id="@+id/tv_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp" />
<!--<com.example.donghe.wxfriend.MultiImageView-->
<!--android:id="@+id/gridview"-->
<!--android:layout_width="match_parent"-->
<!--android:layout_height="wrap_content"-->
<!--android:layout_marginTop="4dp" />-->
<com.lvfq.code.dynamic.view.NineGridTestLayout xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/layout_nine_grid"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
app:sapcing="4dp" />
</LinearLayout>
app:sapcing是設定九宮格中圖片之間的間隔
4.新增資料使用
public List<String> urlList = new ArrayList<>();//圖片url
NineGridTestLayout layout = (NineGridTestLayout) view.findViewById(R.id.layout_nine_grid);
layout.setIsShowAll(false); //當傳入的圖片數超過9張時,是否全部顯示
layout.setSpacing(5); //動態設定圖片之間的間隔
layout.setUrlList(urlList); //最後再設定圖片url
5.RatioImageView :
該類有兩個功能:a.是用於ImageView被按下時有變暗效果
b.ImageView的寬高根據設定的比例動態適配高度,如在xml中設定 app:ratio="2" ,ImageView的高度根據其寬度改變,但始終是寬的2倍,該功能在該專案中沒有使用
/**
* 根據寬高比例自動計算高度ImageView
*/
public class RatioImageView extends ImageView {
/**
* 寬高比例
*/
private float mRatio = 0f;
public RatioImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public RatioImageView(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RatioImageView);
mRatio = typedArray.getFloat(R.styleable.RatioImageView_ratio, 0f);
typedArray.recycle();
}
public RatioImageView(Context context) {
super(context);
}
/**
* 設定ImageView的寬高比
*
* @param ratio
*/
public void setRatio(float ratio) {
mRatio = ratio;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = MeasureSpec.getSize(widthMeasureSpec);
if (mRatio != 0) {
float height = width / mRatio;
heightMeasureSpec = MeasureSpec.makeMeasureSpec((int) height, MeasureSpec.EXACTLY);
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Drawable drawable = getDrawable();
if (drawable != null) {
drawable.mutate().setColorFilter(Color.GRAY,
PorterDuff.Mode.MULTIPLY);
}
break;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
Drawable drawableUp = getDrawable();
if (drawableUp != null) {
drawableUp.mutate().clearColorFilter();
}
break;
}
return super.onTouchEvent(event);
}
}
6.初始化 ImageLoader
import android.app.Application;
import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.ImageLoaderConfiguration;
public class AppApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
initImageLoader();
}
private void initImageLoader() {
ImageLoaderConfiguration configuration = ImageLoaderConfiguration.createDefault(this);
ImageLoader.getInstance().init(configuration);
}
}
7.values中的atts檔案:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="RatioImageView">
<attr name="ratio" format="float"/>
</declare-styleable>
<declare-styleable name="NineGridLayout">
<attr name="sapcing" format="dimension"/>
</declare-styleable>
</resources>
8.ImageLoaderUtil
import android.content.Context;
import android.graphics.Bitmap;
import android.widget.ImageView;
import com.example.donghe.wxfriend.R;
import com.nostra13.universalimageloader.core.DisplayImageOptions;
import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.listener.ImageLoadingListener;
public class ImageLoaderUtil {
public static ImageLoader getImageLoader(Context context) {
return ImageLoader.getInstance();
}
public static DisplayImageOptions getPhotoImageOption() {
Integer extra = 1;
DisplayImageOptions options = new DisplayImageOptions.Builder().cacheInMemory(true).cacheOnDisk(true)
.showImageForEmptyUri(R.drawable.banner_default).showImageOnFail(R.drawable.banner_default)
.showImageOnLoading(R.drawable.banner_default)
.extraForDownloader(extra)
.bitmapConfig(Bitmap.Config.RGB_565).build();
return options;
}
public static void displayImage(Context context, ImageView imageView, String url, DisplayImageOptions options) {
getImageLoader(context).displayImage(url, imageView, options);
}
public static void displayImage(Context context, ImageView imageView, String url, DisplayImageOptions options, ImageLoadingListener listener) {
getImageLoader(context).displayImage(url, imageView, options, listener);
}
}
最後在lib中新增上ImageLoader的jar即可