效果圖
GitHUb地址:https://github.com/HMY314/NineGridLayout
圖片展示形式
1、當只有1張圖時,可以自己定製圖片寬高,也可以使用默認九宮格的寬高;
2、當只有4張圖時,以2*2的方式顯示;
3、除以上兩種情況下,都是按照3列方式顯示,但這時有一些細節:
a、如果只有9張圖,當然是以3*3的方式顯示;
b、如果超過9張圖,可以設置是否全部顯示。
如果設置不完全顯示,則按照3*3的方式顯示,但是在第9張圖上會有一個帶“+”號的數字,
代表還有幾張沒有顯示,這裏是模仿了QQ空間圖片超出9張的顯示方式;
如果設置全部顯示,理所當然的將所有圖片都顯示出來。
4、圖片被按下時,會有一個變暗的效果,這也是模仿微信朋友圈的效果。
項目中使用
111111,自定義控件NineGridLayout的創建
該控件爲自定義控件,因此不需要添加什麼依賴或者作爲Library引入,只需要按照如下方式定義一個NineGridLayout的抽象類,然後按照項目需求創建該抽象類的子類即可。
1,創建抽象類NineGridLayout,繼承自ViewGroup,代碼如下:
public abstract class NineGridLayout extends ViewGroup {
// 默認圖片之間的間隙爲3f
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 List<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_spacing, 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, List<String> urlList);
}
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);
}
}
3,添加該自定義控件用到的兩個自定義屬性,在res/values/attrs.xml中添加如下代碼:
<?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="spacing" format="dimension"/>
</declare-styleable>
</resources>
222222,開始使用上面的自定義九宮格控件NineGridLayout
1,繼承我們的自定義控件抽象類定義對應的子類,實現裏面的三個方法,代碼如下所示,GitHub官網上給出的例子使用的圖片加載框架是ImageLoader,這裏我採用使用Glide的方式,關於Glide框架監聽在實際項目中的使用,不理解的同學可以去看我的博客:安卓項目實戰之Glide高手養成(三):Glide的回調與監聽
public class MyNineGridLayout extends NineGridLayout {
protected static final int MAX_W_H_RATIO = 3;
public MyNineGridLayout(Context context) {
super(context);
}
public MyNineGridLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected boolean displayOneImage(final RatioImageView imageView, String url, final int parentWidth) {
SimpleTarget<Bitmap> simpleTarget = new SimpleTarget<Bitmap>() {
@Override
public void onResourceReady(Bitmap bitmap, GlideAnimation glideAnimation) {
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);
// 加載圖片
imageView.setImageBitmap(bitmap);
}
};
Glide.with(mContext)
.load(url)
.asBitmap()
.placeholder(R.mipmap.ic_launcher)
.error(R.mipmap.ic_launcher)
.into(simpleTarget);
return false;
}
@Override
protected void displayImage(RatioImageView imageView, String url) {
Glide.with(mContext)
.load(url)
.placeholder(R.mipmap.ic_launcher)
.error(R.mipmap.ic_launcher)
.into(imageView);
}
@Override
protected void onClickImage(int position, String url, List<String> urlList) {
Toast.makeText(mContext, "點擊了圖片" + url, Toast.LENGTH_SHORT).show();
}
}
上面displayOneImage方法中定義了一張圖片展示時的寬高規則,可根據自己的項目需求實時進行修改。
2,在佈局文件中引用,如下:
<com.gpf.myrecycle.MyNineGridLayout
android:id="@+id/layout_nine_grid"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
app:spacing="4dp" />
marginTop是指距離上面文字內容的間距。
spacing是指圖片之間的間隙大小,如果不設置,那麼默認爲3dp。
3,在activity中:
public class ShowImageActivity extends AppCompatActivity {
private MyNineGridLayout nineLayout;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_show_image);
List<String> urlList = new ArrayList<>();//圖片url
urlList.add("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1542424359059&di=057513de42837398c02cfbec846ba573&imgtype=0&src=http%3A%2F%2Fg.hiphotos.baidu.com%2Fzhidao%2Fpic%2Fitem%2Fb7003af33a87e95093aa72d714385343faf2b4c8.jpg");
urlList.add("https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=1288537894,3626100649&fm=26&gp=0.jpg");
urlList.add("https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=2891948847,1197284497&fm=26&gp=0.jpg");
urlList.add("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1542424447686&di=bc26b822194056db05d05a9dbb9a3747&imgtype=jpg&src=http%3A%2F%2Fimg4.imgtn.bdimg.com%2Fit%2Fu%3D4013122459%2C1672649128%26fm%3D214%26gp%3D0.jpg");
urlList.add("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1542424359059&di=057513de42837398c02cfbec846ba573&imgtype=0&src=http%3A%2F%2Fg.hiphotos.baidu.com%2Fzhidao%2Fpic%2Fitem%2Fb7003af33a87e95093aa72d714385343faf2b4c8.jpg");
urlList.add("https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=1288537894,3626100649&fm=26&gp=0.jpg");
urlList.add("https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=2891948847,1197284497&fm=26&gp=0.jpg");
urlList.add("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1542424447686&di=bc26b822194056db05d05a9dbb9a3747&imgtype=jpg&src=http%3A%2F%2Fimg4.imgtn.bdimg.com%2Fit%2Fu%3D4013122459%2C1672649128%26fm%3D214%26gp%3D0.jpg");
urlList.add("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1542424359059&di=057513de42837398c02cfbec846ba573&imgtype=0&src=http%3A%2F%2Fg.hiphotos.baidu.com%2Fzhidao%2Fpic%2Fitem%2Fb7003af33a87e95093aa72d714385343faf2b4c8.jpg");
urlList.add("https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=1288537894,3626100649&fm=26&gp=0.jpg");
urlList.add("https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=2891948847,1197284497&fm=26&gp=0.jpg");
nineLayout = (MyNineGridLayout) findViewById(R.id.layout_nine_grid);
nineLayout.setIsShowAll(false); //當傳入的圖片數超過9張時,是否全部顯示
nineLayout.setSpacing(5); //動態設置圖片之間的間隔
nineLayout.setUrlList(urlList); //最後再設置圖片url
}
}
至此就完成了自定義九宮格顯示圖片的效果。