添加项目
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
<manifest
|
||||
package="com.lzy.ninegrid"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||
|
||||
<application>
|
||||
<activity
|
||||
android:name="com.lzy.ninegrid.preview.ImagePreviewActivity"
|
||||
android:theme="@android:style/Theme.Translucent.NoTitleBar"/>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
@@ -0,0 +1,86 @@
|
||||
package com.lzy.ninegrid;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* ================================================
|
||||
* 作 者:廖子尧
|
||||
* 版 本:1.0
|
||||
* 创建日期:2016/3/21
|
||||
* 描 述:
|
||||
* 修订历史:
|
||||
* ================================================
|
||||
*/
|
||||
public class ImageInfo implements Serializable {
|
||||
public String thumbnailUrl;
|
||||
public String bigImageUrl;
|
||||
public int imageViewHeight;
|
||||
public int imageViewWidth;
|
||||
public int imageViewX;
|
||||
public int imageViewY;
|
||||
|
||||
public String getThumbnailUrl() {
|
||||
if (thumbnailUrl==null||thumbnailUrl.length()==0) {
|
||||
if (bigImageUrl!=null&&bigImageUrl.length()>0) {
|
||||
return bigImageUrl;
|
||||
}
|
||||
}
|
||||
return thumbnailUrl;
|
||||
}
|
||||
|
||||
public void setThumbnailUrl(String thumbnailUrl) {
|
||||
this.thumbnailUrl = thumbnailUrl;
|
||||
}
|
||||
|
||||
public String getBigImageUrl() {
|
||||
return bigImageUrl;
|
||||
}
|
||||
|
||||
public void setBigImageUrl(String bigImageUrl) {
|
||||
this.bigImageUrl = bigImageUrl;
|
||||
}
|
||||
|
||||
public int getImageViewHeight() {
|
||||
return imageViewHeight;
|
||||
}
|
||||
|
||||
public void setImageViewHeight(int imageViewHeight) {
|
||||
this.imageViewHeight = imageViewHeight;
|
||||
}
|
||||
|
||||
public int getImageViewWidth() {
|
||||
return imageViewWidth;
|
||||
}
|
||||
|
||||
public void setImageViewWidth(int imageViewWidth) {
|
||||
this.imageViewWidth = imageViewWidth;
|
||||
}
|
||||
|
||||
public int getImageViewX() {
|
||||
return imageViewX;
|
||||
}
|
||||
|
||||
public void setImageViewX(int imageViewX) {
|
||||
this.imageViewX = imageViewX;
|
||||
}
|
||||
|
||||
public int getImageViewY() {
|
||||
return imageViewY;
|
||||
}
|
||||
|
||||
public void setImageViewY(int imageViewY) {
|
||||
this.imageViewY = imageViewY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ImageInfo{" +
|
||||
"imageViewY=" + imageViewY +
|
||||
", imageViewX=" + imageViewX +
|
||||
", imageViewWidth=" + imageViewWidth +
|
||||
", imageViewHeight=" + imageViewHeight +
|
||||
", bigImageUrl='" + bigImageUrl + '\'' +
|
||||
", thumbnailUrl='" + thumbnailUrl + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,252 @@
|
||||
package com.lzy.ninegrid;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Bitmap;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.TypedValue;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class NineGridView extends ViewGroup {
|
||||
|
||||
public static final int MODE_FILL = 0; //填充模式,类似于微信
|
||||
public static final int MODE_GRID = 1; //网格模式,类似于QQ,4张图会 2X2布局
|
||||
|
||||
private static ImageLoader mImageLoader; //全局的图片加载器(必须设置,否者不显示图片)
|
||||
|
||||
private int singleImageSize = 250; // 单张图片时的最大大小,单位dp
|
||||
private float singleImageRatio = 1.0f; // 单张图片的宽高比(宽/高)
|
||||
private int maxImageSize = 9; // 最大显示的图片数
|
||||
private int gridSpacing = 3; // 宫格间距,单位dp
|
||||
private int mode = MODE_FILL; // 默认使用fill模式
|
||||
|
||||
private int columnCount; // 列数
|
||||
private int rowCount; // 行数
|
||||
private int gridWidth; // 宫格宽度
|
||||
private int gridHeight; // 宫格高度
|
||||
|
||||
private List<ImageView> imageViews;
|
||||
private List<ImageInfo> mImageInfo;
|
||||
private NineGridViewAdapter mAdapter;
|
||||
|
||||
public NineGridView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public NineGridView(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public NineGridView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
|
||||
DisplayMetrics dm = context.getResources().getDisplayMetrics();
|
||||
gridSpacing = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, gridSpacing, dm);
|
||||
singleImageSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, singleImageSize, dm);
|
||||
|
||||
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.NineGridView);
|
||||
gridSpacing = (int) a.getDimension(R.styleable.NineGridView_ngv_gridSpacing, gridSpacing);
|
||||
singleImageSize = a.getDimensionPixelSize(R.styleable.NineGridView_ngv_singleImageSize, singleImageSize);
|
||||
singleImageRatio = a.getFloat(R.styleable.NineGridView_ngv_singleImageRatio, singleImageRatio);
|
||||
maxImageSize = a.getInt(R.styleable.NineGridView_ngv_maxSize, maxImageSize);
|
||||
mode = a.getInt(R.styleable.NineGridView_ngv_mode, mode);
|
||||
a.recycle();
|
||||
|
||||
imageViews = new ArrayList<>();
|
||||
}
|
||||
|
||||
public void setColumnCount(int columnCount) {
|
||||
this.columnCount = columnCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
int width = MeasureSpec.getSize(widthMeasureSpec);
|
||||
int height = 0;
|
||||
int totalWidth = width - getPaddingLeft() - getPaddingRight();
|
||||
if (mImageInfo != null && mImageInfo.size() > 0) {
|
||||
if (mImageInfo.size() == 1) {
|
||||
gridWidth = singleImageSize > totalWidth ? totalWidth : singleImageSize;
|
||||
gridHeight = (int) (gridWidth / singleImageRatio);
|
||||
//矫正图片显示区域大小,不允许超过最大显示范围
|
||||
if (gridHeight > singleImageSize) {
|
||||
float ratio = singleImageSize * 1.0f / gridHeight;
|
||||
gridWidth = (int) (gridWidth * ratio);
|
||||
gridHeight = singleImageSize;
|
||||
}
|
||||
} else {
|
||||
gridWidth = gridHeight = (totalWidth - gridSpacing * (columnCount - 1)) / columnCount;
|
||||
// //这里无论是几张图片,宽高都按总宽度的 1/3
|
||||
// gridWidth = gridHeight = (totalWidth - gridSpacing * 2) / 3;
|
||||
}
|
||||
width = gridWidth * columnCount + gridSpacing * (columnCount - 1) + getPaddingLeft() + getPaddingRight();
|
||||
height = gridHeight * rowCount + gridSpacing * (rowCount - 1) + getPaddingTop() + getPaddingBottom();
|
||||
}
|
||||
setMeasuredDimension(width, height);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int l, int t, int r, int b) {
|
||||
if (mImageInfo == null) return;
|
||||
int childrenCount = mImageInfo.size();
|
||||
for (int i = 0; i < childrenCount; i++) {
|
||||
ImageView childrenView = (ImageView) getChildAt(i);
|
||||
|
||||
int rowNum = i / columnCount;
|
||||
int columnNum = i % columnCount;
|
||||
int left = (gridWidth + gridSpacing) * columnNum + getPaddingLeft();
|
||||
int top = (gridHeight + gridSpacing) * rowNum + getPaddingTop();
|
||||
int right = left + gridWidth;
|
||||
int bottom = top + gridHeight;
|
||||
childrenView.layout(left, top, right, bottom);
|
||||
|
||||
if (mImageLoader != null) {
|
||||
if (mImageInfo.get(i).thumbnailUrl!=null&&mImageInfo.get(i).thumbnailUrl.length()>0) {
|
||||
mImageLoader.onDisplayImage(getContext(), childrenView, mImageInfo.get(i).thumbnailUrl);
|
||||
}else{
|
||||
mImageLoader.onDisplayImage(getContext(), childrenView, mImageInfo.get(i).bigImageUrl);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 设置适配器 */
|
||||
public void setAdapter(@NonNull NineGridViewAdapter adapter) {
|
||||
mAdapter = adapter;
|
||||
List<ImageInfo> imageInfo = adapter.getImageInfo();
|
||||
|
||||
if (imageInfo == null || imageInfo.isEmpty()) {
|
||||
setVisibility(GONE);
|
||||
return;
|
||||
} else {
|
||||
setVisibility(VISIBLE);
|
||||
}
|
||||
|
||||
int imageCount = imageInfo.size();
|
||||
if (maxImageSize > 0 && imageCount > maxImageSize) {
|
||||
imageInfo = imageInfo.subList(0, maxImageSize);
|
||||
imageCount = imageInfo.size(); //再次获取图片数量
|
||||
}
|
||||
if(columnCount == 0){
|
||||
columnCount = 3;
|
||||
}
|
||||
//默认是3列显示,行数根据图片的数量决定
|
||||
rowCount = imageCount / columnCount + (imageCount % columnCount == 0 ? 0 : 1);
|
||||
// columnCount = 3;
|
||||
//grid模式下,显示4张使用2X2模式
|
||||
if (mode == MODE_GRID) {
|
||||
if (imageCount == 4) {
|
||||
rowCount = 2;
|
||||
columnCount = 2;
|
||||
}
|
||||
}
|
||||
|
||||
//保证View的复用,避免重复创建
|
||||
if (mImageInfo == null) {
|
||||
for (int i = 0; i < imageCount; i++) {
|
||||
ImageView iv = getImageView(i);
|
||||
if (iv == null) return;
|
||||
addView(iv, generateDefaultLayoutParams());
|
||||
}
|
||||
} else {
|
||||
int oldViewCount = mImageInfo.size();
|
||||
int newViewCount = imageCount;
|
||||
if (oldViewCount > newViewCount) {
|
||||
removeViews(newViewCount, oldViewCount - newViewCount);
|
||||
} else if (oldViewCount < newViewCount) {
|
||||
for (int i = oldViewCount; i < newViewCount; i++) {
|
||||
ImageView iv = getImageView(i);
|
||||
if (iv == null) return;
|
||||
addView(iv, generateDefaultLayoutParams());
|
||||
}
|
||||
}
|
||||
}
|
||||
//修改最后一个条目,决定是否显示更多
|
||||
if (adapter.getImageInfo().size() > maxImageSize) {
|
||||
View child = getChildAt(maxImageSize - 1);
|
||||
if (child instanceof NineGridViewWrapper) {
|
||||
NineGridViewWrapper imageView = (NineGridViewWrapper) child;
|
||||
imageView.setMoreNum(adapter.getImageInfo().size() - maxImageSize);
|
||||
}
|
||||
}
|
||||
mImageInfo = imageInfo;
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
/** 获得 ImageView 保证了 ImageView 的重用 */
|
||||
private ImageView getImageView(final int position) {
|
||||
ImageView imageView;
|
||||
if (position < imageViews.size()) {
|
||||
imageView = imageViews.get(position);
|
||||
} else {
|
||||
imageView = mAdapter.generateImageView(getContext());
|
||||
imageView.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
mAdapter.onImageItemClick(getContext(), NineGridView.this, position, mAdapter.getImageInfo());
|
||||
}
|
||||
});
|
||||
imageViews.add(imageView);
|
||||
}
|
||||
return imageView;
|
||||
}
|
||||
|
||||
/** 设置宫格间距 */
|
||||
public void setGridSpacing(int spacing) {
|
||||
gridSpacing = spacing;
|
||||
}
|
||||
|
||||
/** 设置只有一张图片时的宽 */
|
||||
public void setSingleImageSize(int maxImageSize) {
|
||||
singleImageSize = maxImageSize;
|
||||
}
|
||||
|
||||
/** 设置只有一张图片时的宽高比 */
|
||||
public void setSingleImageRatio(float ratio) {
|
||||
singleImageRatio = ratio;
|
||||
}
|
||||
|
||||
/** 设置最大图片数 */
|
||||
public void setMaxSize(int maxSize) {
|
||||
maxImageSize = maxSize;
|
||||
}
|
||||
|
||||
public int getMaxSize() {
|
||||
return maxImageSize;
|
||||
}
|
||||
|
||||
public static void setImageLoader(ImageLoader imageLoader) {
|
||||
mImageLoader = imageLoader;
|
||||
}
|
||||
|
||||
public static ImageLoader getImageLoader() {
|
||||
return mImageLoader;
|
||||
}
|
||||
|
||||
public interface ImageLoader {
|
||||
/**
|
||||
* 需要子类实现该方法,以确定如何加载和显示图片,并且设置给NineGridView
|
||||
*
|
||||
* @param context 上下文
|
||||
* @param imageView 需要展示图片的ImageView
|
||||
* @param url 图片地址
|
||||
*/
|
||||
void onDisplayImage(Context context, ImageView imageView, String url);
|
||||
|
||||
/**
|
||||
* @param url 图片的地址
|
||||
* @return 当前框架的本地缓存图片
|
||||
*/
|
||||
Bitmap getCacheImage(String url);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.lzy.ninegrid;
|
||||
|
||||
import android.content.Context;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class NineGridViewAdapter implements Serializable {
|
||||
|
||||
protected Context context;
|
||||
private List<ImageInfo> imageInfo;
|
||||
|
||||
public NineGridViewAdapter(Context context, List<ImageInfo> imageInfo) {
|
||||
this.context = context;
|
||||
this.imageInfo = imageInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果要实现图片点击的逻辑,重写此方法即可
|
||||
*
|
||||
* @param context 上下文
|
||||
* @param nineGridView 九宫格控件
|
||||
* @param index 当前点击图片的的索引
|
||||
* @param imageInfo 图片地址的数据集合
|
||||
*/
|
||||
protected void onImageItemClick(Context context, NineGridView nineGridView, int index, List<ImageInfo> imageInfo) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成ImageView容器的方式,默认使用NineGridImageViewWrapper类,即点击图片后,图片会有蒙板效果
|
||||
* 如果需要自定义图片展示效果,重写此方法即可
|
||||
*
|
||||
* @param context 上下文
|
||||
* @return 生成的 ImageView
|
||||
*/
|
||||
protected ImageView generateImageView(Context context) {
|
||||
NineGridViewWrapper imageView = new NineGridViewWrapper(context);
|
||||
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
|
||||
imageView.setImageResource(R.drawable.ic_default_color);
|
||||
return imageView;
|
||||
}
|
||||
|
||||
public List<ImageInfo> getImageInfo() {
|
||||
return imageInfo;
|
||||
}
|
||||
|
||||
public void setImageInfoList(List<ImageInfo> imageInfo) {
|
||||
this.imageInfo = imageInfo;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
package com.lzy.ninegrid;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.text.TextPaint;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.TypedValue;
|
||||
import android.view.MotionEvent;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import androidx.core.view.ViewCompat;
|
||||
|
||||
public class NineGridViewWrapper extends ImageView {
|
||||
|
||||
private int moreNum = 0; //显示更多的数量
|
||||
private int maskColor = 0x88000000; //默认的遮盖颜色
|
||||
private float textSize = 35; //显示文字的大小单位sp
|
||||
private int textColor = 0xFFFFFFFF; //显示文字的颜色
|
||||
|
||||
private TextPaint textPaint; //文字的画笔
|
||||
private String msg = ""; //要绘制的文字
|
||||
|
||||
public NineGridViewWrapper(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public NineGridViewWrapper(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public NineGridViewWrapper(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
|
||||
//转化单位
|
||||
textSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, textSize, getContext().getResources().getDisplayMetrics());
|
||||
|
||||
textPaint = new TextPaint();
|
||||
textPaint.setTextAlign(Paint.Align.CENTER); //文字居中对齐
|
||||
textPaint.setAntiAlias(true); //抗锯齿
|
||||
textPaint.setTextSize(textSize); //设置文字大小
|
||||
textPaint.setColor(textColor); //设置文字颜色
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
if (moreNum > 0) {
|
||||
canvas.drawColor(maskColor);
|
||||
float baseY = getHeight() / 2 - (textPaint.ascent() + textPaint.descent()) / 2;
|
||||
canvas.drawText(msg, getWidth() / 2, baseY, textPaint);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
|
||||
switch (event.getAction()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
Drawable drawable = getDrawable();
|
||||
if (drawable != null) {
|
||||
/**
|
||||
* 默认情况下,所有的从同一资源(R.drawable.XXX)加载来的drawable实例都共享一个共用的状态,
|
||||
* 如果你更改一个实例的状态,其他所有的实例都会收到相同的通知。
|
||||
* 使用使 mutate 可以让这个drawable变得状态不定。这个操作不能还原(变为不定后就不能变为原来的状态)。
|
||||
* 一个状态不定的drawable可以保证它不与其他任何一个drawabe共享它的状态。
|
||||
* 此处应该是要使用的 mutate(),但是在部分手机上会出现点击后变白的现象,所以没有使用
|
||||
* 目前这种解决方案没有问题
|
||||
*/
|
||||
// drawable.mutate().setColorFilter(Color.GRAY, PorterDuff.Mode.MULTIPLY);
|
||||
drawable.setColorFilter(Color.GRAY, PorterDuff.Mode.MULTIPLY);
|
||||
ViewCompat.postInvalidateOnAnimation(this);
|
||||
}
|
||||
break;
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
break;
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
case MotionEvent.ACTION_UP:
|
||||
Drawable drawableUp = getDrawable();
|
||||
if (drawableUp != null) {
|
||||
// drawableUp.mutate().clearColorFilter();
|
||||
drawableUp.clearColorFilter();
|
||||
ViewCompat.postInvalidateOnAnimation(this);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return super.onTouchEvent(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow();
|
||||
setImageDrawable(null);
|
||||
}
|
||||
|
||||
public int getMoreNum() {
|
||||
return moreNum;
|
||||
}
|
||||
|
||||
public void setMoreNum(int moreNum) {
|
||||
this.moreNum = moreNum;
|
||||
msg = "+" + moreNum;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public int getMaskColor() {
|
||||
return maskColor;
|
||||
}
|
||||
|
||||
public void setMaskColor(int maskColor) {
|
||||
this.maskColor = maskColor;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public float getTextSize() {
|
||||
return textSize;
|
||||
}
|
||||
|
||||
public void setTextSize(float textSize) {
|
||||
this.textSize = textSize;
|
||||
textPaint.setTextSize(textSize);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public int getTextColor() {
|
||||
return textColor;
|
||||
}
|
||||
|
||||
public void setTextColor(int textColor) {
|
||||
this.textColor = textColor;
|
||||
textPaint.setColor(textColor);
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.lzy.ninegrid.preview;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
import androidx.viewpager.widget.ViewPager;
|
||||
|
||||
/** 修复图片在ViewPager控件中缩放报错的BUG */
|
||||
public class HackyViewPager extends ViewPager {
|
||||
|
||||
public HackyViewPager(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public HackyViewPager(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent ev) {
|
||||
try {
|
||||
return super.onTouchEvent(ev);
|
||||
} catch (IllegalArgumentException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onInterceptTouchEvent(MotionEvent ev) {
|
||||
try {
|
||||
return super.onInterceptTouchEvent(ev);
|
||||
} catch (IllegalArgumentException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,233 @@
|
||||
package com.lzy.ninegrid.preview;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.View;
|
||||
import android.view.ViewTreeObserver;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.viewpager.widget.ViewPager;
|
||||
|
||||
import com.lzy.ninegrid.ImageInfo;
|
||||
import com.lzy.ninegrid.R;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ImagePreviewActivity extends Activity implements ViewTreeObserver.OnPreDrawListener {
|
||||
|
||||
public static final String IMAGE_INFO = "IMAGE_INFO";
|
||||
public static final String CURRENT_ITEM = "CURRENT_ITEM";
|
||||
public static final int ANIMATE_DURATION = 200;
|
||||
|
||||
private RelativeLayout rootView;
|
||||
|
||||
private ImagePreviewAdapter imagePreviewAdapter;
|
||||
private List<ImageInfo> imageInfo;
|
||||
private int currentItem;
|
||||
private int imageHeight;
|
||||
private int imageWidth;
|
||||
private int screenWidth;
|
||||
private int screenHeight;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_preview);
|
||||
|
||||
ViewPager viewPager = (ViewPager) findViewById(R.id.viewPager);
|
||||
final TextView tv_pager = (TextView) findViewById(R.id.tv_pager);
|
||||
rootView = (RelativeLayout) findViewById(R.id.rootView);
|
||||
|
||||
DisplayMetrics metric = new DisplayMetrics();
|
||||
getWindowManager().getDefaultDisplay().getMetrics(metric);
|
||||
screenWidth = metric.widthPixels;
|
||||
screenHeight = metric.heightPixels;
|
||||
|
||||
Intent intent = getIntent();
|
||||
imageInfo = (List<ImageInfo>) intent.getSerializableExtra(IMAGE_INFO);
|
||||
currentItem = intent.getIntExtra(CURRENT_ITEM, 0);
|
||||
|
||||
imagePreviewAdapter = new ImagePreviewAdapter(this, imageInfo);
|
||||
viewPager.setAdapter(imagePreviewAdapter);
|
||||
viewPager.setCurrentItem(currentItem);
|
||||
viewPager.getViewTreeObserver().addOnPreDrawListener(this);
|
||||
viewPager.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
|
||||
@Override
|
||||
public void onPageSelected(int position) {
|
||||
currentItem = position;
|
||||
tv_pager.setText(String.format(getString(R.string.select), currentItem + 1, imageInfo.size()));
|
||||
}
|
||||
});
|
||||
tv_pager.setText(String.format(getString(R.string.select), currentItem + 1, imageInfo.size()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
finishActivityAnim();
|
||||
}
|
||||
|
||||
/** 绘制前开始动画 */
|
||||
@Override
|
||||
public boolean onPreDraw() {
|
||||
rootView.getViewTreeObserver().removeOnPreDrawListener(this);
|
||||
final View view = imagePreviewAdapter.getPrimaryItem();
|
||||
final ImageView imageView = imagePreviewAdapter.getPrimaryImageView();
|
||||
computeImageWidthAndHeight(imageView);
|
||||
|
||||
final ImageInfo imageData = imageInfo.get(currentItem);
|
||||
final float vx = imageData.imageViewWidth * 1.0f / imageWidth;
|
||||
final float vy = imageData.imageViewHeight * 1.0f / imageHeight;
|
||||
ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1.0f);
|
||||
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
|
||||
@Override
|
||||
public void onAnimationUpdate(ValueAnimator animation) {
|
||||
long duration = animation.getDuration();
|
||||
long playTime = animation.getCurrentPlayTime();
|
||||
float fraction = duration > 0 ? (float) playTime / duration : 1f;
|
||||
if (fraction > 1) fraction = 1;
|
||||
view.setTranslationX(evaluateInt(fraction, imageData.imageViewX + imageData.imageViewWidth / 2 - imageView.getWidth() / 2, 0));
|
||||
view.setTranslationY(evaluateInt(fraction, imageData.imageViewY + imageData.imageViewHeight / 2 - imageView.getHeight() / 2, 0));
|
||||
view.setScaleX(evaluateFloat(fraction, vx, 1));
|
||||
view.setScaleY(evaluateFloat(fraction, vy, 1));
|
||||
view.setAlpha(fraction);
|
||||
rootView.setBackgroundColor(evaluateArgb(fraction, Color.TRANSPARENT, Color.BLACK));
|
||||
}
|
||||
});
|
||||
addIntoListener(valueAnimator);
|
||||
valueAnimator.setDuration(ANIMATE_DURATION);
|
||||
valueAnimator.start();
|
||||
return true;
|
||||
}
|
||||
|
||||
/** activity的退场动画 */
|
||||
public void finishActivityAnim() {
|
||||
final View view = imagePreviewAdapter.getPrimaryItem();
|
||||
final ImageView imageView = imagePreviewAdapter.getPrimaryImageView();
|
||||
computeImageWidthAndHeight(imageView);
|
||||
|
||||
final ImageInfo imageData = imageInfo.get(currentItem);
|
||||
final float vx = imageData.imageViewWidth * 1.0f / imageWidth;
|
||||
final float vy = imageData.imageViewHeight * 1.0f / imageHeight;
|
||||
final ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1.0f);
|
||||
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
|
||||
@Override
|
||||
public void onAnimationUpdate(ValueAnimator animation) {
|
||||
long duration = animation.getDuration();
|
||||
long playTime = animation.getCurrentPlayTime();
|
||||
float fraction = duration > 0 ? (float) playTime / duration : 1f;
|
||||
if (fraction > 1) fraction = 1;
|
||||
view.setTranslationX(evaluateInt(fraction, 0, imageData.imageViewX + imageData.imageViewWidth / 2 - imageView.getWidth() / 2));
|
||||
view.setTranslationY(evaluateInt(fraction, 0, imageData.imageViewY + imageData.imageViewHeight / 2 - imageView.getHeight() / 2));
|
||||
view.setScaleX(evaluateFloat(fraction, 1, vx));
|
||||
view.setScaleY(evaluateFloat(fraction, 1, vy));
|
||||
view.setAlpha(1 - fraction);
|
||||
rootView.setBackgroundColor(evaluateArgb(fraction, Color.BLACK, Color.TRANSPARENT));
|
||||
}
|
||||
});
|
||||
addOutListener(valueAnimator);
|
||||
valueAnimator.setDuration(ANIMATE_DURATION);
|
||||
valueAnimator.start();
|
||||
}
|
||||
|
||||
/** 计算图片的宽高 */
|
||||
private void computeImageWidthAndHeight(ImageView imageView) {
|
||||
|
||||
// 获取真实大小
|
||||
Drawable drawable = imageView.getDrawable();
|
||||
int intrinsicHeight = drawable.getIntrinsicHeight();
|
||||
int intrinsicWidth = drawable.getIntrinsicWidth();
|
||||
// 计算出与屏幕的比例,用于比较以宽的比例为准还是高的比例为准,因为很多时候不是高度没充满,就是宽度没充满
|
||||
float h = screenHeight * 1.0f / intrinsicHeight;
|
||||
float w = screenWidth * 1.0f / intrinsicWidth;
|
||||
if (h > w) h = w;
|
||||
else w = h;
|
||||
|
||||
// 得出当宽高至少有一个充满的时候图片对应的宽高
|
||||
imageHeight = (int) (intrinsicHeight * h);
|
||||
imageWidth = (int) (intrinsicWidth * w);
|
||||
}
|
||||
|
||||
/** 进场动画过程监听 */
|
||||
private void addIntoListener(ValueAnimator valueAnimator) {
|
||||
valueAnimator.addListener(new Animator.AnimatorListener() {
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation) {
|
||||
rootView.setBackgroundColor(0x0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationCancel(Animator animation) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animator animation) {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** 退场动画过程监听 */
|
||||
private void addOutListener(ValueAnimator valueAnimator) {
|
||||
valueAnimator.addListener(new Animator.AnimatorListener() {
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation) {
|
||||
rootView.setBackgroundColor(0x0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
finish();
|
||||
overridePendingTransition(0, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationCancel(Animator animation) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animator animation) {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** Integer 估值器 */
|
||||
public Integer evaluateInt(float fraction, Integer startValue, Integer endValue) {
|
||||
int startInt = startValue;
|
||||
return (int) (startInt + fraction * (endValue - startInt));
|
||||
}
|
||||
|
||||
/** Float 估值器 */
|
||||
public Float evaluateFloat(float fraction, Number startValue, Number endValue) {
|
||||
float startFloat = startValue.floatValue();
|
||||
return startFloat + fraction * (endValue.floatValue() - startFloat);
|
||||
}
|
||||
|
||||
/** Argb 估值器 */
|
||||
public int evaluateArgb(float fraction, int startValue, int endValue) {
|
||||
int startA = (startValue >> 24) & 0xff;
|
||||
int startR = (startValue >> 16) & 0xff;
|
||||
int startG = (startValue >> 8) & 0xff;
|
||||
int startB = startValue & 0xff;
|
||||
|
||||
int endA = (endValue >> 24) & 0xff;
|
||||
int endR = (endValue >> 16) & 0xff;
|
||||
int endG = (endValue >> 8) & 0xff;
|
||||
int endB = endValue & 0xff;
|
||||
|
||||
return (startA + (int) (fraction * (endA - startA))) << 24//
|
||||
| (startR + (int) (fraction * (endR - startR))) << 16//
|
||||
| (startG + (int) (fraction * (endG - startG))) << 8//
|
||||
| (startB + (int) (fraction * (endB - startB)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
package com.lzy.ninegrid.preview;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.ProgressBar;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.viewpager.widget.PagerAdapter;
|
||||
|
||||
import com.lzy.ninegrid.ImageInfo;
|
||||
import com.lzy.ninegrid.NineGridView;
|
||||
import com.lzy.ninegrid.R;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import uk.co.senab.photoview.PhotoView;
|
||||
import uk.co.senab.photoview.PhotoViewAttacher;
|
||||
|
||||
/**
|
||||
* ================================================
|
||||
* 作 者:廖子尧
|
||||
* 版 本:1.0
|
||||
* 创建日期:2016/3/21
|
||||
* 描 述:
|
||||
* 修订历史:
|
||||
* ================================================
|
||||
*/
|
||||
public class ImagePreviewAdapter extends PagerAdapter implements PhotoViewAttacher.OnPhotoTapListener {
|
||||
|
||||
private List<ImageInfo> imageInfo;
|
||||
private Context context;
|
||||
private View currentView;
|
||||
|
||||
public ImagePreviewAdapter(Context context, @NonNull List<ImageInfo> imageInfo) {
|
||||
super();
|
||||
this.imageInfo = imageInfo;
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return imageInfo.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isViewFromObject(View view, Object object) {
|
||||
return view == object;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPrimaryItem(ViewGroup container, int position, Object object) {
|
||||
super.setPrimaryItem(container, position, object);
|
||||
currentView = (View) object;
|
||||
}
|
||||
|
||||
public View getPrimaryItem() {
|
||||
return currentView;
|
||||
}
|
||||
|
||||
public ImageView getPrimaryImageView() {
|
||||
return (ImageView) currentView.findViewById(R.id.pv);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object instantiateItem(ViewGroup container, int position) {
|
||||
View view = LayoutInflater.from(context).inflate(R.layout.item_photoview, container, false);
|
||||
final ProgressBar pb = (ProgressBar) view.findViewById(R.id.pb);
|
||||
final PhotoView imageView = (PhotoView) view.findViewById(R.id.pv);
|
||||
|
||||
ImageInfo info = this.imageInfo.get(position);
|
||||
imageView.setOnPhotoTapListener(this);
|
||||
showExcessPic(info, imageView);
|
||||
|
||||
//如果需要加载的loading,需要自己改写,不能使用这个方法
|
||||
NineGridView.getImageLoader().onDisplayImage(view.getContext(), imageView, info.bigImageUrl);
|
||||
|
||||
// pb.setVisibility(View.VISIBLE);
|
||||
// Glide.with(context).load(info.bigImageUrl)//
|
||||
// .placeholder(R.drawable.ic_default_image)//
|
||||
// .error(R.drawable.ic_default_image)//
|
||||
// .diskCacheStrategy(DiskCacheStrategy.ALL)//
|
||||
// .listener(new RequestListener<String, GlideDrawable>() {
|
||||
// @Override
|
||||
// public boolean onException(Exception e, String model, Target<GlideDrawable> target, boolean isFirstResource) {
|
||||
// pb.setVisibility(View.GONE);
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public boolean onResourceReady(GlideDrawable resource, String model, Target<GlideDrawable> target, boolean isFromMemoryCache, boolean isFirstResource) {
|
||||
// pb.setVisibility(View.GONE);
|
||||
// return false;
|
||||
// }
|
||||
// }).into(imageView);
|
||||
|
||||
container.addView(view);
|
||||
return view;
|
||||
}
|
||||
|
||||
/** 展示过度图片 */
|
||||
private void showExcessPic(ImageInfo imageInfo, PhotoView imageView) {
|
||||
//先获取大图的缓存图片
|
||||
Bitmap cacheImage = NineGridView.getImageLoader().getCacheImage(imageInfo.bigImageUrl);
|
||||
//如果大图的缓存不存在,在获取小图的缓存
|
||||
if (cacheImage == null) cacheImage = NineGridView.getImageLoader().getCacheImage(imageInfo.thumbnailUrl);
|
||||
//如果没有任何缓存,使用默认图片,否者使用缓存
|
||||
if (cacheImage == null) {
|
||||
imageView.setImageResource(R.drawable.ic_default_color);
|
||||
} else {
|
||||
imageView.setImageBitmap(cacheImage);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroyItem(ViewGroup container, int position, Object object) {
|
||||
container.removeView((View) object);
|
||||
}
|
||||
|
||||
/** 单击屏幕关闭 */
|
||||
@Override
|
||||
public void onPhotoTap(View view, float x, float y) {
|
||||
((ImagePreviewActivity) context).finishActivityAnim();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package com.lzy.ninegrid.preview;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
|
||||
import com.lzy.ninegrid.ImageInfo;
|
||||
import com.lzy.ninegrid.NineGridView;
|
||||
import com.lzy.ninegrid.NineGridViewAdapter;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* ================================================
|
||||
* 作 者:廖子尧
|
||||
* 版 本:1.0
|
||||
* 创建日期:2016/3/21
|
||||
* 描 述:
|
||||
* 修订历史:
|
||||
* ================================================
|
||||
*/
|
||||
public class NineGridViewClickAdapter extends NineGridViewAdapter {
|
||||
|
||||
private int statusHeight;
|
||||
|
||||
public NineGridViewClickAdapter(Context context, List<ImageInfo> imageInfo) {
|
||||
super(context, imageInfo);
|
||||
statusHeight = getStatusHeight(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onImageItemClick(Context context, NineGridView nineGridView, int index, List<ImageInfo> imageInfo) {
|
||||
for (int i = 0; i < imageInfo.size(); i++) {
|
||||
ImageInfo info = imageInfo.get(i);
|
||||
View imageView;
|
||||
if (i < nineGridView.getMaxSize()) {
|
||||
imageView = nineGridView.getChildAt(i);
|
||||
} else {
|
||||
//如果图片的数量大于显示的数量,则超过部分的返回动画统一退回到最后一个图片的位置
|
||||
imageView = nineGridView.getChildAt(nineGridView.getMaxSize() - 1);
|
||||
}
|
||||
info.imageViewWidth = imageView.getWidth();
|
||||
info.imageViewHeight = imageView.getHeight();
|
||||
int[] points = new int[2];
|
||||
imageView.getLocationInWindow(points);
|
||||
info.imageViewX = points[0];
|
||||
info.imageViewY = points[1] - statusHeight;
|
||||
}
|
||||
|
||||
Intent intent = new Intent(context, ImagePreviewActivity.class);
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putSerializable(ImagePreviewActivity.IMAGE_INFO, (Serializable) imageInfo);
|
||||
bundle.putInt(ImagePreviewActivity.CURRENT_ITEM, index);
|
||||
intent.putExtras(bundle);
|
||||
context.startActivity(intent);
|
||||
((Activity) context).overridePendingTransition(0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得状态栏的高度
|
||||
*/
|
||||
public int getStatusHeight(Context context) {
|
||||
int statusHeight = -1;
|
||||
try {
|
||||
Class<?> clazz = Class.forName("com.android.internal.R$dimen");
|
||||
Object object = clazz.newInstance();
|
||||
int height = Integer.parseInt(clazz.getField("status_bar_height").get(object).toString());
|
||||
statusHeight = context.getResources().getDimensionPixelSize(height);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return statusHeight;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<solid android:color="@color/ic_default_bg_color"/>
|
||||
</shape>
|
||||
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout
|
||||
android:id="@+id/rootView"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<com.lzy.ninegrid.preview.HackyViewPager
|
||||
android:id="@+id/viewPager"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_pager"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_marginBottom="20dp"
|
||||
android:text="1/4"
|
||||
android:textColor="#ffffff"
|
||||
android:textSize="20sp"/>
|
||||
</RelativeLayout>
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<uk.co.senab.photoview.PhotoView
|
||||
android:id="@+id/pv"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"/>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/pb"
|
||||
style="@style/Widget.AppCompat.ProgressBar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:visibility="gone"/>
|
||||
</FrameLayout>
|
||||
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<declare-styleable name="NineGridView">
|
||||
<attr name="ngv_singleImageSize" format="dimension"/>
|
||||
<attr name="ngv_singleImageRatio" format="float"/>
|
||||
<attr name="ngv_gridSpacing" format="dimension"/>
|
||||
<attr name="ngv_maxSize" format="integer"/>
|
||||
<attr name="ngv_mode" format="enum">
|
||||
<enum name="fill" value="0"/>
|
||||
<enum name="grid" value="1"/>
|
||||
</attr>
|
||||
</declare-styleable>
|
||||
</resources>
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="ic_default_bg_color">#33000000</color>
|
||||
</resources>
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="select">%1$s/%2$s</string>
|
||||
</resources>
|
||||
Reference in New Issue
Block a user