Android自定义图片浏览View

功能简介
  1. URL图片列表左右滑动,缩放预览。
  2. 图片预加载,浏览当前,预加载下一张,否则不加载。
效果展示

QQ视频20230211091427

代码实现
package com.example.framedemo.view.view0004;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

import androidx.annotation.Nullable;

import com.example.framedemo.view.viewParent.BaseView;

import java.util.ArrayList;
import java.util.List;

/**
 * 图片浏览专用View
 * @Author 绝命三郎
 */
public class PictureBrowseView extends BaseView {

    public PictureBrowseView(Context context) {
        this(context,null);
    }

    public PictureBrowseView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs,0);
    }

    private View view;
    private List<Bitmap> bitmapList =new ArrayList<>();
    public PictureBrowseView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    private float widthSize;
    private float heightSize;
    private Paint bitmapPaint;
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        widthSize=MeasureSpec.getSize(widthMeasureSpec);
        heightSize=MeasureSpec.getSize(heightMeasureSpec);

        bitmapPaint=new Paint();
        bitmapPaint.setStrokeWidth(0);

        zoomPointX=widthSize/2f;
        zoomPointY=heightSize/2f;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        for (int k=0;k<bitmapList.size();k++){

            Bitmap thisBitmap=bitmapList.get(k);
            float bitmapWidth=thisBitmap.getWidth();
            float bitmapHeight=thisBitmap.getHeight();

            float step=widthSize/bitmapWidth;

            //裁剪图片中的部分(此处:全图)
            Rect rect=new Rect(0, 0,(int)bitmapWidth,(int)bitmapHeight);

            float left=0+(k*widthSize)+moveValue;
            float top=(heightSize/2f)-((bitmapHeight*step)/2f);
            float right=widthSize+(k*widthSize)+moveValue;
            float bottom=(heightSize/2f)+((bitmapHeight*step)/2f);

            float proportionalValue=bitmapHeight/bitmapWidth;//图片宽高比例值

            if (k==-sourceIndex){//缩放当前图片

                float widthZoomValue= (float) thisZoomValue;
                float heightZoomValue=widthZoomValue*proportionalValue;

                //显示在屏幕什么位置
                RectF rectF=new RectF(
                        (float) (left-widthZoomValue),
                        (float) (top-heightZoomValue),
                        (float) (right+widthZoomValue),
                        (float) (bottom+heightZoomValue));
                canvas.drawBitmap(thisBitmap,rect,rectF,bitmapPaint);
            }else {//其余图片不用缩放

                //显示在屏幕什么位置
                RectF rectF=new RectF((float) left, (float) top, (float) right, (float) bottom);
                canvas.drawBitmap(thisBitmap,rect,rectF,bitmapPaint);
            }

        }

    }

    private float downX;
    float moveValue=0;//所有的滑动值累加
    float oldMoveValue=0;
    private int sourceIndex=0;//资源索引值

    private double nLenStart;
    private double nLenEnd;

    private boolean zoomActionTag=false;//缩放事件标签:事件锁定为缩放事件时为true, 非缩放为false
    private float zoomPointX;//缩放点X
    private float zoomPointY;//缩放点Y
    @Override
    public boolean onTouchEvent(MotionEvent event) {

        int pCount = event.getPointerCount();// 触摸设备时手指的数量

        int action  = event.getAction();// 获取触屏动作。比如:按下、移动和抬起等手势动作

        boolean isZoomTouch=(action & MotionEvent.ACTION_MASK)== MotionEvent.ACTION_POINTER_DOWN && pCount == 2;
        boolean isZoomTouchMove=(action & MotionEvent.ACTION_MASK)== MotionEvent.ACTION_MOVE && pCount == 2;

        // 手势按下且屏幕上是两个手指数量时
        if (isZoomTouch){

            //锁定为缩放事件
            zoomActionTag=true;

            zoomPointX=event.getX();
            zoomPointY=event.getY();

            // 获取按下时候两个坐标的x轴的水平距离,取绝对值
            int xLen = Math.abs((int)event.getX(0) - (int)event.getX(1));
            // 获取按下时候两个坐标的y轴的水平距离,取绝对值
            int yLen = Math.abs((int)event.getY(0) - (int)event.getY(1));

            // 根据x轴和y轴的水平距离,求平方和后再开方获取两个点之间的直线距离。此时就获取到了两个手指刚按下时的直线距离
            nLenStart = Math.sqrt((double) xLen * xLen + (double) yLen * yLen);

        }else if (isZoomTouchMove){// 手势滑动或抬起且屏幕上是两个手指数量时

            // 获取抬起时候两个坐标的x轴的水平距离,取绝对值
            int xLen = Math.abs((int)event.getX(0) - (int)event.getX(1));
            // 获取抬起时候两个坐标的y轴的水平距离,取绝对值
            int yLen = Math.abs((int)event.getY(0) - (int)event.getY(1));

            // 根据x轴和y轴的水平距离,求平方和后再开方获取两个点之间的直线距离。此时就获取到了两个手指抬起时的直线距离
            nLenEnd = Math.sqrt((double) xLen * xLen + (double) yLen * yLen);

            // 根据手势按下时两个手指触点之间的直线距离A和手势抬起时两个手指触点之间的直线距离B。比较A和B的大小,得出用户是手势放大还是手势缩小
            if(nLenEnd > nLenStart){
                //Toast.makeText(this, "手势放大", Toast.LENGTH_SHORT).show();
                Log.d("fxHou","手势滑动放大");
                zoomMethod(nLenEnd-nLenStart);
                return true;
            }else if(nLenEnd < nLenStart){
                //Toast.makeText(this, "手势缩小", Toast.LENGTH_SHORT).show();
                Log.d("fxHou","手势滑动缩小");
                zoomMethod(nLenEnd-nLenStart);
                return true;
            }

        }

        //释放缩放事件
        if (event.getAction()==MotionEvent.ACTION_UP && zoomActionTag){
            zoomActionTag=false;
            this.thisZoomValue =0;
            postInvalidate();
            return true;
        }

        //往下是处理左右滑动和单击
        if (!zoomActionTag){
            if (event.getAction()==MotionEvent.ACTION_DOWN){
                Log.d("fxHou","按下");
                downX=event.getX();
                oldMoveValue=moveValue;
                return true;
            }

            if (event.getAction()==MotionEvent.ACTION_MOVE){
                moveValue=oldMoveValue+(event.getX()-downX);
                postInvalidate();
                Log.d("fxHou","滑动");
            }

            if (event.getAction()==MotionEvent.ACTION_UP){
                Log.d("fxHou","抬起 sourceIndex="+sourceIndex);
                if ((event.getX()-downX)>100){
                    //手指向右滑:
                    if (sourceIndex!=0)sourceIndex=sourceIndex+1;
                    moveValue=widthSize*(sourceIndex);
                    postInvalidate();
                }else if ((event.getX()-downX)<-100){
                    //手指向左滑:
                    postInvalidate();
                    if (sourceIndex!=-(bitmapList.size()-1))sourceIndex=sourceIndex-1;
                    moveValue=widthSize*(sourceIndex);
                }else {
                    //单击事件:
                    Log.d("fxHou","单击");
                }
                return true;
            }
        }

        return super.onTouchEvent(event);
    }

    //缩放大事件(缩放大值)
    private double thisZoomValue=0;
    private void zoomMethod(double thisZoomValue) {
        this.thisZoomValue =thisZoomValue;
        postInvalidate();
    }

    //通过Url转换为Bitmap
    public void setUrl(List<String> urlList) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (String url: urlList){
                    urlToBitmap(url);
                }
            }
        }).start();
    }

    //直接给Bitmap
    public void setBitmap(List<Bitmap> bitmapList) {
        this.bitmapList=bitmapList;
    }

    private void urlToBitmap(String url){
        //使用Glide框架将URL转为Bitmap(Glide自带缓存)   注: 请自行引入Glide依赖,此处不做讲解
        try {
            Bitmap bitmap = Glide.with(getContext())
                    .asBitmap()
                    .load(url)
                    .submit(900, 1200).get();
            if (bitmap!=null) {
                if (!bitmapList.contains(bitmap))bitmapList.add(bitmap);
                Log.d("fxHou","Bitmap Size="+bitmapList.size());
                BaseCacheFactory.getInstance().putBitMap(url,bitmap);//缓存bitmap
            }
            postInvalidate();
        } catch (ExecutionException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

使用方法
  1. 在XML中使用VIEW
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#323232"
    tools:context=".controller.activity.MainActivity">

    <com.example.framedemo.view.view0004.PictureBrowseView
        android:id="@+id/pictureView"
        android:background="@color/black"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</androidx.constraintlayout.widget.ConstraintLayout>
  1. 导入数据
public class MainActivity extends BaseActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        List<String> urlList =new ArrayList<>();
        urlList.add("http://www.juemingsanlang.com/source/Public/OfficialPicture/202208142210061006.jpg");
        urlList.add("http://www.juemingsanlang.com/source/Public/OfficialPicture/202208142211011008.jpg");
        urlList.add("http://www.juemingsanlang.com/source/Public/OfficialPicture/202208142212241010.jpg");
        urlList.add("http://www.juemingsanlang.com/source/Public/OfficialPicture/202209011116431012.jpg");
        urlList.add("http://www.juemingsanlang.com/source/Public/OfficialPicture/202209011425001016.jpg");
        urlList.add("http://www.juemingsanlang.com/source/Public/OfficialPicture/202209011721231020.jpg");

        PictureBrowseView pictureView=(PictureBrowseView) findViewById(R.id.pictureView);
        pictureView.setUrl(urlList);

    }
}

完成!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

绝命三郎

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值