Android—仿QQ空间动态九宫格图片预览(一)

Android—仿QQ空间动态九宫格图片预览(一)

image

星光不负赶路人,时间不负有心人,每当你在感叹,如果有这样一个东西就好了,请注意其实这是你的机会。

最近在做课程设计 ? ,发现自己要实现一个类似QQ 动态里面的图片九宫格预览效果的控件? ,自己本着大佬造轮子,自己在轮子上面改善,达到自己要到效果,结果搞了几天才搞好,达到自己想要的效果,这是一个一波三折的过程,自己也看了很多源码,虽然很多也是似懂非懂的,毕竟是初学者,对很多底层适配器不是很懂? ,但是终于被我搞出来了,顺便写一篇博客记录一下。

NineGridImageView(九宫格图片控件)

这是我在网上找到一个大佬的自定义的组件,我试了一下

NineGridImageView(九宫格图片控件)

预览效果

image

特性

  • 设置图片之间的间隔

    app:imgGap="4dp"nineGridImageView.setGap(int gap);

  • 设置最大图片数:

    app:maxSize="9" 或者 nineGridImageView.setMaxSize(int maxSize)

    如果最大图片数小于等于0,则没有图片数的限制。

  • 设置显示样式

    app:showStyle="fill"nineGridImageView.setShowStyle(int style);

    默认样式是网格样式:STYLE_GRID

    另外一种样式是:STYLE_FILL

  • 当只有一张图的时候,可以设置其显示大小,不让其显示的过小:

    app:singleImgSize="120dp"nineGridImageView.setSingleImgSize(int singleImgSize)

用法

  1. 首先添加依赖

    implement 'com.jaeger.ninegridimageview:library:1.0.2'
    implementation 'com.github.bumptech.glide:glide:4.12.0'
    annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0'
    

    image

  2. AndroidManifest.xml添加权限

    <uses-permission android:name="android.permission.INTERNET"/>
        <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
        <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    
    android:requestLegacyExternalStorage="true"
            android:usesCleartextTraffic="true"
    

    image

  3. 在布局文件中添加 NineGridImageView, 如下所示:

    <com.jaeger.ninegridimageview.NineGridImageView
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_height="wrap_content"
        android:layout_margin="16dp"
        android:layout_width="match_parent"
        app:imgGap="4dp"
        app:showStyle="fill"
        app:singleImgSize="120dp"/>
    
  4. 为 NineGridImageView 设置 NineGridImageViewAdapter

    nineGridImageView.setAdapter(nineGridViewAdapter);
    

    下面是 NineGridImageViewAdapter.class 的源码:

    public abstract class NineGridImageViewAdapter<T> {
    
        protected abstract void onDisplayImage(Context context, ImageView imageView, T t);
    
        protected void onItemImageClick(Context context, int index, List<T> list) {
    
        }
    
        protected ImageView generateImageView(Context context) {
            GridImageView imageView = new GridImageView(context);
            imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
            return imageView;
        }
    }
    
    • T 是你图片的数据类型, 你可以简单的使用 String 类型也可以是你自定义的类型;
    • 你必须重写 onDisplayImage(Context context, ImageView imageView, T t) 方法去设置显示图片的方式, 你可以使用 Picasso、Glide 、ImageLoader 或者其他的图片加载库,你也可以给 ImageView 设置一个占位图;
    • 如果你需要处理图片的点击事件,你可以重写 onItemImageClick(Context context, int index, List<T> list) 方法,加上你自己的处理逻辑;
    • 如果你要使用自定义的 ImageView,你可以重写 generateImageView(Context context) 方法, 去生成自定的 ImageView

    下面是一段示例代码:

    private NineGridImageViewAdapter<Photo> mAdapter = new NineGridImageViewAdapter<Photo>() {
        @Override
        protected void onDisplayImage(Context context, ImageView imageView, Photo photo) {
            Picasso.with(context)
                .load(photo.getSmallUrl)
                .placeholder(R.drawable.ic_default_image)
                .into(imageView);
        }
        @Override
        protected ImageView generateImageView(Context context) {
            return super.generateImageView(context);
        }
        @Override
        protected void onItemImageClick(Context context, int index, List<Photo> photoList) {
            showBigPicture(context, photoList.get(index).getBigUrl());
        }
    
               
    ...
    	mNineGridImageView.setAdapter(mAdapter);
    ...
    
  5. 给 NineGridImageView 设置图片数据:

    nineGridImageView.setImagesData(List<T> imageDataList);
    

demo代码

MainActivity

package com.huncm.review1;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Context;
import android.os.Bundle;
import android.widget.ImageView;
import android.widget.Toast;

import com.bumptech.glide.Glide;
import com.jaeger.library.StatusBarUtil;
import com.jaeger.ninegridimageview.NineGridImageView;
import com.jaeger.ninegridimageview.NineGridImageViewAdapter;

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

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        List<String> list = new ArrayList<>();
        list.add("https://cdn.jsdelivr.net/gh/Yqifei/Blog-Image@master/20211026/image.6719h9mvs700.png");
        list.add("https://img-blog.csdnimg.cn/img_convert/8dc4978a27ba3ccdaa697c2d7814f3ac.png");
        list.add("https://img-blog.csdnimg.cn/img_convert/52f5dd5a02ab958c9a2b4daa825925e5.png");
        list.add("https://cdn.jsdelivr.net/gh/Yqifei/Blog-Image@master/20211026/image.6719h9mvs700.png");
        list.add("https://img-blog.csdnimg.cn/img_convert/8dc4978a27ba3ccdaa697c2d7814f3ac.png");

        NineGridImageView nineGridImageView = findViewById(R.id.nineGridImageView);
        nineGridImageView.setAdapter(myadpter);
        nineGridImageView.setImagesData( list);
    }

    private NineGridImageViewAdapter <String> myadpter = new NineGridImageViewAdapter<String>() {
        @Override
        protected void onDisplayImage(Context context, ImageView imageView, String url) {
            Glide.with(context).load(url).into(imageView);
        }

        @Override
        protected void onItemImageClick(Context context, int index, List<String> list) {
            super.onItemImageClick(context, index, list);
        }

        @Override
        protected ImageView generateImageView(Context context) {
            return super.generateImageView(context);
        }
    };
}

activity_main.xml

<?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"
    tools:context=".MainActivity">

    <com.jaeger.ninegridimageview.NineGridImageView
        android:id="@+id/nineGridImageView"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_margin="16dp"
        android:layout_marginStart="16dp"
        android:layout_marginLeft="16dp"
        android:layout_marginEnd="16dp"
        android:layout_marginRight="16dp"
        app:imgGap="4dp"
        app:maxSize="9"
        app:showStyle="grid"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

当我实现这个控件的时候,我发现他不能点击图片放大,图片不能预览,而且不能左滑右滑?,我的卑微又开始了,我就去网上找了如何实现点击方大

这时出现了第二个控件:PhotoView

PhotoView控件

简单的介绍

这是一个图片查看库,实现图片浏览功能,支持pinch(捏合)手势或者点击放大缩小。支持在ViewPager中翻页浏览图片。
PhotoView 是一款扩展自Android ImageView ,支持通过单点/多点触摸来进行图片缩放的智能控件。功能实用和强大。

特性

可以用于查看图片,并对图片进行拖动缩放,拖动过程中不会出现边缘空白;
双击缩小放大,Fling移动,并支持上述过程的渐变;
在放大情况下也支持viewpager等的拖动切换;
支持多击事件检测,单机,双击事件;
支持各种回调给调用者;

效果预览

image

使用

  1. 在根文件(不是模块文件)中添加此:build.gradle

    allprojects {
        repositories {
            maven { url "https://www.jitpack.io" }
        }
    }
    
    buildscript {
        repositories {
            maven { url "https://www.jitpack.io" }
        }	
    }
    
  2. 导入依赖

        implementation 'com.github.chrisbanes:PhotoView:2.0.0'
    
  3. 编写布局文件

    <?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"
        tools:context=".MainActivity2">
    
        <com.github.chrisbanes.photoview.PhotoView
            android:id="@+id/photo_view"
            android:layout_width="0dp"
            android:layout_height="0dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            tools:ignore="MissingClass" />
    
    </androidx.constraintlayout.widget.ConstraintLayout>
    
  4. 编写逻辑代码

    package com.huncm.review1;
    
    import androidx.appcompat.app.AppCompatActivity;
    
    import android.os.Bundle;
    
    
    import com.bumptech.glide.Glide;
    import com.github.chrisbanes.photoview.PhotoView;
    
    import javax.microedition.khronos.opengles.GL;
    
    public class MainActivity2 extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main2);
            PhotoView photoView =  findViewById(R.id.photo_view);
            Glide.with(this).load("https://img-blog.csdnimg.cn/img_convert/52f5dd5a02ab958c9a2b4daa825925e5.png").into(photoView);
        }
    }
    

写到这里QQ九宫格也算是实现了一半,如果只是这两个控件的话,实现的QQ九宫格图片不能预览,也不能左滑右滑,我便在网上搜索了一波。找到一种解决方法:使用ViewPager和PhotoView实现图片浏览

使用ViewPager和PhotoView实现图片浏览

使用photoView实现图片的放大缩小,再使用viewPager实现图片的左右滑动

image

添加依赖

  • build.gradle (app)
  //photoView
    implementation 'com.github.chrisbanes:PhotoView:2.0.0'
    //glide
    implementation 'com.github.bumptech.glide:glide:4.9.0'
    annotationProcessor 'androidx.annotation:annotation:1.0.0'
    annotationProcessor 'com.github.bumptech.glide:compiler:4.9.0'
  • build.gradle (project)
allprojects {
    repositories {
        google()
        jcenter()
        maven { url "https://jitpack.io" }
    }
}
  • manifests 添加网络权限
    <uses-permission android:name="android.permission.INTERNET"/>

XML文件

  • activity_main.xml
<?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"
    tools:context=".MainActivity">

    <androidx.viewpager.widget.ViewPager
        android:id="@+id/viewpager"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="#99000000"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"></androidx.viewpager.widget.ViewPager>

    <LinearLayout
        android:id="@+id/points"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_alignBottom="@+id/viewpager"
        android:gravity="center"
        android:orientation="horizontal"
        android:padding="5dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="@+id/viewpager">

    </LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

使用Glide

这里使用Glide来加载网络图片,Gilde自从4.0.0版本开始要通过生成GlideApp类来使用各种Api,那么就开始动手实践吧

  1. 生成GlideApp

    • 首先创建MyAppGlideModule类

      
      @GlideModule
      public final class MyAppGlideModule extends AppGlideModule {
      }
      
    • build ->Rebuild Project
      image

  2. 封装ImageLoading类

    因为我这里只是一个测试Demo,所以这是封装了一种方法,Glide的 功能很强大,如加载gif图, 实现模糊 等等操作,这里插入一个大佬写的Glide专栏

    Android图片加载框架最全解析----Glide

    public class ImageLoader {
        public static void display(Context context, ImageView imageView,String url){
            GlideApp
                    .with(context)
                    .load(url)
                    .placeholder(R.drawable.timg)
                    .into(imageView);
        }
    
    }
    

Activity层操作

在这一层需要初始化Viewpager和设置白点指示器, 并和适配器绑定数据

package com.huncm.photoviewdemo;

import androidx.appcompat.app.AppCompatActivity;
import androidx.viewpager.widget.ViewPager;

import android.os.Bundle;
import android.widget.ImageView;
import android.widget.LinearLayout;

public class MainActivity extends AppCompatActivity {

    private ViewPager viewPager;
    private MyViewPagerAdapter myViewPagerAdapter;

    private String[] imglist = {
            "https://cdn.jsdelivr.net/gh/Yqifei/Blog-Image@master/20210427/image.2c9e9xfoi3b4.png",
            "https://cdn.jsdelivr.net/gh/Yqifei/Blog-Image@master/20210427/image.17irwo5suuxs.png",
            "https://cdn.jsdelivr.net/gh/Yqifei/Blog-Image@master/20210427/image.2oi4dcxsc1c0.png",
            "https://cdn.jsdelivr.net/gh/Yqifei/Blog-Image@master/20210427/image.2m80368jvr80.png",
            "https://cdn.jsdelivr.net/gh/Yqifei/Blog-Image@master/20210427/image.65n8rjffvk00.png",
            "https://cdn.jsdelivr.net/gh/Yqifei/Blog-Image@master/20210427/image.75iaxbwd0fk0.png"
    };
    private LinearLayout points;
    private int prePosition;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initView();
        initData();
    }

    private void initData() {
        viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float v, int i1) {
                position = position % imglist.length;
                //把前一个白变为黑
                points.getChildAt(prePosition).setBackgroundResource(R.drawable.point_back);
                //把当前白点变为黑点
                points.getChildAt(position).setBackgroundResource(R.drawable.point_white);
                //记录下当前位置(当前位置变白后,赋值给前一个点)
                prePosition = position;
            }

            @Override
            public void onPageSelected(int i) {

            }

            @Override
            public void onPageScrollStateChanged(int i) {

            }
        });
    }

    private void initView() {
        viewPager = findViewById(R.id.viewpager);
        myViewPagerAdapter = new MyViewPagerAdapter(this,imglist);
        viewPager.setAdapter(myViewPagerAdapter);
        points = findViewById(R.id.points);
        for(int i = 0;i<imglist.length;i++) {
            //白点
            //根据viewPager的数量,添加白点指示器
            ImageView view = new ImageView(this);
            view.setBackgroundResource(R.drawable.point_back);
            //给点设置宽高
            LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(20, 20);
            //给控件设置边距
            params.leftMargin = 10;
            //给view设置参数
            view.setLayoutParams(params);
            //将图片添加到线性布局中
            points.addView(view);
        }

        points.getChildAt(0).setBackgroundResource(R.drawable.point_white);
        viewPager.setCurrentItem(0);
    }
}

适配器配置

这里把需要浏览的图片地址数组传递过去,然后通过Gilde加载网络图片, 并以PhotoView代替ImageView实现放大缩小功能

package com.huncm.photoviewdemo;

import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;

import androidx.viewpager.widget.PagerAdapter;

import com.github.chrisbanes.photoview.PhotoView;

public class MyViewPagerAdapter extends PagerAdapter {
    private String[] imgList;
    private Context context;
    public MyViewPagerAdapter(Context context,String[] imgList){
        this.imgList = imgList;
        this.context = context;
    }


    @Override
    public int getCount() {
        return imgList.length;
    }

    //指定复用的判断逻辑,固定写法:view == object
    @Override
    public boolean isViewFromObject(View view, Object object) {
        //当创建新的条目,又反回来,判断view是否可以被复用(即是否存在)
        return view == object;
    }

    //返回要显示的条目内容
    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        //container  容器  相当于用来存放imageView

        PhotoView photoView = new PhotoView(context);

        photoView.setScaleType(ImageView.ScaleType.FIT_CENTER);

        ImageLoader.display(context,photoView,imgList[position]);

        //把图片添加到container中
        container.addView(photoView);
        //把图片返回给框架,用来缓存
        return photoView;
    }

    //销毁条目
    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        //object:刚才创建的对象,即要销毁的对象
        container.removeView((View) object);
    }
}

Demo地址

接下文: Android—仿QQ空间动态九宫格图片预览(二)

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>