保姆式RecyclerView下拉刷新、上拉加载更多Kotlin

本文详细介绍了使用Kotlin在Android中实现RecyclerView的下拉刷新和上拉加载更多功能,包括Glide框架加载网络图片、保存图片至相册、更新图库以及分享到微信的步骤。同时,讨论了Android不同版本下图片存储的策略,并提供了相关代码说明和补充工具类。

网络图片加载显示、保存、更新到图库、分享到微信

开发步骤

思路步骤

安卓10以上:下载图片到私有空间(沙盒),从私有空间复制到公共空间,然后通知相册更新
直接定义好下载的图片路径
通过glide框架下载图片
把下载的图片从沙盒文件中,复制到手机的Pictures公共目录中
相册才可以访问到
安卓10以下:需要文件存储权限,直接存储到相册路径中
先申明文件存储权限
然后定义好图片路径
通过glide框架下载图片
下载完毕后直接通知相册更新即可

代码说明

1、清单文件授权


<uses-permission android:name="android.permission.INTERNET" /><!--联网-->

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /><!--读-->

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /><!--写-->

2、Glide加载图片

submit()方法必须在子线程中使用,只会下载图片,不会展示图片
get( ),获取下载到的图片路径
asBitmap(指定为bitmap格式),asFile(指定为File格式)

 bitmap = Glide.with(context).asBitmap().load(url).submit().get()

 Glide.with(context).load(imageUrl).placeholder(imagePlace).into(this)

3、保存到相册

loadImage(context, imageUrl)//保存到相册

4、分享到微信

UmengShareLib.shareNetPic(context, imageUrl)

5、xml中直接引用url加载

<ImageView

           android:id="@+id/iv_picture"

           imageUrl="@{url}"

           android:layout_width="162dp"

           android:layout_height="158dp"

           android:layout_gravity="center"

           android:layout_marginLeft="65dp"

           android:layout_marginRight="65dp"

           android:scaleType="centerInside" />

补充工具类


/**
 * 获取路径
 */
fun getFilePath(): File {
    //版本适配
    return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        //定义路径
        File(InitApp.initApp.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)?.absolutePath + "/" + System.currentTimeMillis() + ".png")
    } else {
        File(
            Environment.getExternalStorageDirectory()
                .toString() + "/Pictures/sci99" + "/" + System.currentTimeMillis() + ".png"
        )
    }
}

/**
 * 加载图片
 */
fun loadImage(context: Context, url: String,filePath: File?=null) {
    var file= filePath ?: getFilePath()
    val downloadImage = ImageDownload(
        url,
        context,
        0,
        0,
        file,
        object : ImageDownload.ImageDownloadCallBack {
            override fun onDownLoadSuccess(bitmap: Bitmap) {
                updateToMedia(context,file)
            }

            override fun onDownLoadFailed() {
                toast("加载失败")
            }
        })
    Thread(downloadImage).start()
}

/**
 * 下载图片
 * 10以上不需要手动授权
 * 10以下需要手动授权(授权:10以上获取路径方法,10以下获取路径方法)
 */
fun downLoad(context: Context, url: String,filePath: File?=null) {
    com.chem99.composite.kt.checkPermission(context) {
        loadImage(context, url, filePath)
    }
}

/**
 * 更新到图库
 */
fun updateToMedia(context: Context, filePath: File) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        DensityUtil.saveBitmap(context, filePath, filePath.name)
    } else {
        //通知系统相册刷新
        context.sendBroadcast(
            Intent(
                Intent.ACTION_MEDIA_SCANNER_SCAN_FILE,
                Uri.fromFile(filePath)
            )
        )
    }
    toast("图片已下载到手机")
}

object ImageViewBindAdapter {
@JvmStatic
@BindingAdapter(value = ["imageUrl", "imagePlaceholder"], requireAll = false)
fun ImageView.setImageUrl(imageUrl: String, imagePlaceholder: Drawable? = null) {
    val imagePlace =
    imagePlaceholder ?: ContextCompat.getDrawable(context, R.drawable.kf_pic_thumb_bg)
    Glide.with(context).load(imageUrl).placeholder(imagePlace).into(this)
    }
}
import android.content.Context
import android.graphics.Bitmap
import android.os.Handler
import android.os.Looper
import com.bumptech.glide.Glide
import com.bumptech.glide.request.target.Target
import java.io.File
import java.io.FileOutputStream
import java.io.IOException

class ImageDownload(private val url: String, val context: Context, var width: Int, var height: Int, var mFile: File, val callBack: ImageDownloadCallBack) :
        Runnable {

        interface ImageDownloadCallBack {
            fun onDownLoadSuccess(bitmap: Bitmap)
            fun onDownLoadFailed()
        }

        private val mHandler = Handler(Looper.getMainLooper())
        override fun run() {
            var bitmap: Bitmap? = null
            var fos: FileOutputStream? = null
            try {
                if (width == 0) {
                    width = Target.SIZE_ORIGINAL
                }
                if (height == 0) {
                    height = Target.SIZE_ORIGINAL
                }
                bitmap = Glide.with(context).asBitmap().load(url).submit().get()
                if (bitmap != null) {
                    //上级文件夹不存在则创建
                    if (!mFile.parentFile.exists()) {
                        mFile.parentFile.mkdirs()
                    }
                    if (!mFile.exists()) {
                        mFile.createNewFile()
                    }
                    fos = FileOutputStream(mFile)
                    bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos)
                    fos.flush()
                }
            } catch (e: Exception) {
                e.printStackTrace()
            } finally {
                if (fos != null) {
                    try {
                        fos.close()
                    } catch (e: IOException) {
                        e.printStackTrace()
                    }
                }
                if (bitmap != null && mFile.exists()) {
                    val finalBitmap: Bitmap = bitmap
                    mHandler.post { callBack.onDownLoadSuccess(finalBitmap) }
                } else {
                    mHandler.post { callBack.onDownLoadFailed() }
                }
            }
        }
    }
public class UmengShareLib {

    public enum SHARE_SCOPE {
        WEXIN, WEIXIN_CIRCLE, WXWORK, DEFAULT_ALL,WEICHAT_WORK_ALL
    }

    static final SHARE_MEDIA[] displaylist = new SHARE_MEDIA[]
            {
                    SHARE_MEDIA.WEIXIN, SHARE_MEDIA.WEIXIN_CIRCLE,
                    SHARE_MEDIA.QQ, SHARE_MEDIA.QZONE
            };

    static final SHARE_MEDIA[] displaylist1 = new SHARE_MEDIA[]
            {
                    SHARE_MEDIA.WEIXIN, SHARE_MEDIA.WEIXIN_CIRCLE, SHARE_MEDIA.WXWORK,
                    SHARE_MEDIA.QQ, SHARE_MEDIA.QZONE
            };


    public static void share(Context context, UmengShare umengShare, SHARE_SCOPE shareScope) {
        Logger.e("shareWeiXin", umengShare.getLink() + "==" + umengShare.getTitle() + "==" + umengShare.getDescription());
        if (umengShare == null) {
            return;
        }
        UMWeb web = new UMWeb(umengShare.getLink());
        web.setTitle(umengShare.getTitle());//标题
        web.setThumb(new UMImage(context, R.drawable.share_logo));
        web.setDescription(umengShare.getDescription());//描述
        if (umengShare.getUmShareListener() == null) {
            umengShare.setUmShareListener(umShareListener);
        }
        ShareAction shareAction = new ShareAction((Activity) context);
        if (shareScope==SHARE_SCOPE.WEICHAT_WORK_ALL) {
            shareAction.setDisplayList(displaylist1);
            shareAction.withMedia(web).setCallback(umengShare.getUmShareListener()).open();
        } else if(shareScope==SHARE_SCOPE.DEFAULT_ALL){
            shareAction.setDisplayList(displaylist);
            shareAction.withMedia(web).setCallback(umengShare.getUmShareListener()).open();
        }else if(shareScope==SHARE_SCOPE.WEXIN){
            shareAction.setPlatform(SHARE_MEDIA.WEIXIN);
            shareAction.withMedia(web).setCallback(umengShare.getUmShareListener()).share();
        }else if(shareScope==SHARE_SCOPE.WEIXIN_CIRCLE){
            shareAction.setPlatform(SHARE_MEDIA.WEIXIN_CIRCLE);
            shareAction.withMedia(web).setCallback(umengShare.getUmShareListener()).share();
        }else if(shareScope==SHARE_SCOPE.WXWORK){
            shareAction.setPlatform(SHARE_MEDIA.WXWORK);
            shareAction.withMedia(web).setCallback(umengShare.getUmShareListener()).share();
        }

    }

    private static UMShareListener umShareListener = new UMShareListener() {
        @Override
        public void onStart(SHARE_MEDIA share_media) {

        }

        @Override
        public void onResult(SHARE_MEDIA platform) {

        }

        @Override
        public void onError(SHARE_MEDIA platform, Throwable t) {
            if(!TextUtils.isEmpty(t.getMessage())){
                if(t.getMessage().contains("2008")){
                    ToastExtKt.toast("没有安装应用");
                }
            }
        }

        @Override
        public void onCancel(SHARE_MEDIA platform) {

        }
    };



    public static void shareBitmapWeiXin(Context context, int flag, Bitmap bitmap) {
        UMImage image = new UMImage(context, bitmap);//网络图片
        image.compressStyle = UMImage.CompressStyle.SCALE;//大小压缩,默认为大小压缩,适合普通很大的图
        if (0 == flag) {
            new ShareAction((Activity) context).setPlatform(SHARE_MEDIA.WEIXIN)
                    .withMedia(image)
                    .setCallback(umShareListener)
                    .share();
        } else {
            new ShareAction((Activity) context).setPlatform(SHARE_MEDIA.WEIXIN_CIRCLE)
                    .withMedia(image)
                    .setCallback(umShareListener)
                    .share();
        }
    }


    public static void shareLocalPic(Context context, String path) {

        UMImage image = new UMImage(context,
                new File(path));//本地图片
        image.compressStyle = UMImage.CompressStyle.SCALE;//大小压缩,默认为大小压缩,适合普通很大的图
        new ShareAction((Activity) context).setDisplayList(displaylist)
                .withMedia(image)
                .setCallback(umShareListener)
                .open();

    }


    public static void shareNetPic(Context context, String path) {

        UMImage image = new UMImage(context, path);//网络图片
        image.compressStyle = UMImage.CompressStyle.SCALE;//大小压缩,默认为大小压缩,适合普通很大的图
        new ShareAction((Activity) context).setPlatform(SHARE_MEDIA.WEIXIN)
                .withMedia(image)
                .setCallback(umShareListener)
                .share();

    }


}

补充说明

Android图片加载框架——Glide

https://blog.youkuaiyun.com/lfq88/article/details/118553438

图片保存

Bitmap(位即图)
**概念:**用来描述一张图片的长、宽、颜色等信息,通常情况下,我们可以通过BitmapFactory来将某一路径下的图片解析为Bitmap对象
特点:是程序中内存消耗的大户,当Bitmap使用内存超过可用空间时,则会报OOM(out of memory,内存溢出)异常
**compress( ):**使用此方法压缩bitmap(位即图)图片后,图片的宽高大小不会变化,像素大小也不会变化,所以图片在内存中的大小也不会变化

fos : FileOutputStream= FileOutputStream(mFile)

bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos)

fos.flush()
第二个参数是压缩比重,图片存储在磁盘上的大小会根据这个值变化。值越小,存储在磁盘上的图片文件越小。
第一个参数如果是Bitmap.CompressFormat.PNG,那不管第二个值如何变化,图片大小都不会变化,不支持png图片的压缩。
**flush( ):**刷新该流的缓冲(强制写出缓冲区的数据)

将bitmap保存成文件,复制到公共目录,更新至图库

数据存放路径区别

不应该将应用数据直接存放到SD卡的根目录下,当手机安装了大量的app时,SD卡根目录会迅速变的杂乱不堪
App专属文件 vs App独立文件
app专属文件:只有该app才可以使用的文件,例如:专属格式的电子书
app独立文件:不依赖于某特定app的文件,例如:照片

App独立文件

当我们删除应用后,仍保留在手机上的,例如:拍照的照片,不应随着app的删除而被删除。
对于这类文件,Android给我们提供了特定的目录,这些目录都是以DIRECTORY开头的,例如:DIRECTORY_MUSIC,DIRECTORY_PICTURE

App专属文件

这类文件应该是随着app删除而一起删除的,它们可以被存储在两个地方:internal storage 和 external storage 。 internal storage就是手机自带的一块存储区域,通常很小;external storage就是通常所说的SD卡,通常很大,有16GB,32GB等。
internal storage很小,所以你就应该很正确的使用它,因为SD卡有可能会被用户卸下,换成新的,所以SD卡不是任何时间都可用的,因此我们必须将一些重要的数据库文件以及一些用户配置文件存放在internal storage中。将一些大的图片或文件等缓存放到external storage中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值