Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android:NineGridLayout — 仿微信朋友圈和QQ空間的九宮格圖片展示自定義控件

Android:NineGridLayout — 仿微信朋友圈和QQ空間的九宮格圖片展示自定義控件

編輯:關於Android編程

一個仿微信朋友圈和QQ空間的九宮格圖片展示自定義控件。

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、圖片被按下時,會有一個變暗的效果,這也是模仿微信朋友圈的效果。

二、使用方法

 

1、核心類是NineGridLayout,繼承自ViewGroup的抽象類,所以我們實際項目使用需要繼承它,並要實現3個方法,如下:

 

public abstract class NineGridLayout extends ViewGroup {
    //******************************其他代碼省略**************************

        /**
         * 顯示一張圖片
         * @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 urlList);
    }

2、我這裡用NineGridTestLayout繼承NineGridLayout實現,displayOneImage()與displayImage()中的參數都是顯示圖片需要的,我這裡用的是ImageLoader顯示圖片,當然你也可以用其他的。

 

 

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) {

        //這裡是只顯示一張圖片的情況,顯示圖片的寬高可以根據實際圖片大小自由定制,parentWidth 為該layout的寬度
         ImageLoader.getInstance().displayImage(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;// true 代表按照九宮格默認大小顯示(此時不要調用setOneImageLayoutParams);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, List urlList) {
         Toast.makeText(mContext, "點擊了圖片" + url, Toast.LENGTH_SHORT).show();
     }
}

3、在xml中實現

 

 

app:sapcing是設置九宮格中圖片之間的間隔。

 

4、使用:

 

 

    public List 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

 

三、核心類

NineGridLayout.java

 

 

/**
 * 描述:
 * 作者:HMY
 * 時間:2016/5/10
 */
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 List 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 urlList) {
        if (getListSize(urlList) == 0) {
            setVisibility(GONE);
            return;
        }
        setVisibility(VISIBLE);

        mUrlList.clear();
        mUrlList.addAll(urlList);

        if (!mIsFirst) {
            notifyDataSetChanged();
        }
    }

    public void notifyDataSetChanged() {
        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 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 urlList);
}

 

RatioImageView.java

該類有兩個功能:

1、是用於ImageView被按下時有變暗效果

2、ImageView的寬高根據設置的比例動態適配高度,如在xml中設置 app:ratio="2" ,ImageView的高度根據其寬度改變,但始終是寬的2倍,該功能在該項目中沒有使用。

 

/**
 * 根據寬高比例自動計算高度ImageView
 * Created by HMY on 2016/4/21.
 */
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);
    }

}

 

 

代碼可在我的GitHub上下載,地址:https://github.com/HMY314/NineGridLayout


效果圖

\\\\\


 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved