当前位置: 首页 > news >正文

android项目实战之使用框架 集成多图片、视频的上传

效果图

 实现方式,本功能使用PictureSelector 第三方库  。作者项目地址:https://github.com/LuckSiege/PictureSelector

 

1. builder.gradle 增加

implementation 'io.github.lucksiege:pictureselector:v3.11.1'implementation 'com.tbruyelle.rxpermissions2:rxpermissions:0.9.3@aar'
implementation 'io.reactivex.rxjava2:rxjava:2.0.0'

2. XML布局

<RelativeLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"><Viewandroid:layout_width="match_parent"android:layout_height="match_parent"android:layout_alignTop="@+id/recycler"android:layout_alignBottom="@+id/recycler"android:background="@color/app_color_white" /><androidx.recyclerview.widget.RecyclerViewandroid:id="@+id/recycler"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginLeft="8dp"android:layout_marginRight="8dp"android:overScrollMode="never" /></RelativeLayout>

3. 适配器,这里对GridImageAdapter进行了改进。

public class GridImageAdapter extends RecyclerView.Adapter<GridImageAdapter.ViewHolder> {public static final String TAG = "PictureSelector";public static final int TYPE_CAMERA = 1;public static final int TYPE_PICTURE = 2;private final LayoutInflater mInflater;private  ArrayList<LocalMedia> list = new ArrayList<>();private int selectMax = 9;/*** 删除*/public void delete(int position) {try {if (position != RecyclerView.NO_POSITION && list.size() > position) {list.remove(position);notifyItemRemoved(position);notifyItemRangeChanged(position, list.size());}} catch (Exception e) {e.printStackTrace();}}public GridImageAdapter(Context context, List<LocalMedia> result) {this.mInflater = LayoutInflater.from(context);this.list.addAll(result);}public void setSelectMax(int selectMax) {this.selectMax = selectMax;}public void setList(ArrayList<LocalMedia> list) {this.list = list;}public int getSelectMax() {return selectMax;}public ArrayList<LocalMedia> getData() {return list;}public void remove(int position) {if (position < list.size()) {list.remove(position);}}public static class ViewHolder extends RecyclerView.ViewHolder {ImageView mImg;ImageView mIvDel;TextView tvDuration;public ViewHolder(View view) {super(view);mImg = view.findViewById(R.id.fiv);mIvDel = view.findViewById(R.id.iv_del);tvDuration = view.findViewById(R.id.tv_duration);}}@Overridepublic int getItemCount() {if (list.size() < selectMax) {return list.size() + 1;} else {return list.size();}}@Overridepublic int getItemViewType(int position) {if (isShowAddItem(position)) {return TYPE_CAMERA;} else {return TYPE_PICTURE;}}/*** 创建ViewHolder*/@Overridepublic ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {View view = mInflater.inflate(R.layout.item_filter_image, viewGroup, false);return new ViewHolder(view);}private boolean isShowAddItem(int position) {int size = list.size();return position == size;}/*** 设置值*/@Overridepublic void onBindViewHolder(final ViewHolder viewHolder, final int position) {//少于MaxSize张,显示继续添加的图标if (getItemViewType(position) == TYPE_CAMERA) {viewHolder.mImg.setImageResource(R.drawable.ic_add_image);viewHolder.mImg.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {if (mItemClickListener != null) {mItemClickListener.openPicture();}}});viewHolder.mIvDel.setVisibility(View.INVISIBLE);} else {viewHolder.mIvDel.setVisibility(View.VISIBLE);viewHolder.mIvDel.setOnClickListener(view -> {int index = viewHolder.getAbsoluteAdapterPosition();if (index != RecyclerView.NO_POSITION && list.size() > index) {list.remove(index);notifyItemRemoved(index);notifyItemRangeChanged(index, list.size());}});LocalMedia media = list.get(position);int chooseModel = media.getChooseModel();String path = media.getAvailablePath();long duration = media.getDuration();viewHolder.tvDuration.setVisibility(PictureMimeType.isHasVideo(media.getMimeType())? View.VISIBLE : View.GONE);if (chooseModel == SelectMimeType.ofAudio()) {viewHolder.tvDuration.setVisibility(View.VISIBLE);viewHolder.tvDuration.setCompoundDrawablesRelativeWithIntrinsicBounds(R.drawable.ps_ic_audio, 0, 0, 0);} else {viewHolder.tvDuration.setCompoundDrawablesRelativeWithIntrinsicBounds(R.drawable.ps_ic_video, 0, 0, 0);}viewHolder.tvDuration.setText(DateUtils.formatDurationTime(duration));if (chooseModel == SelectMimeType.ofAudio()) {viewHolder.mImg.setImageResource(com.luck.picture.lib.R.drawable.ps_audio_placeholder);} else {RequestOptions options = RequestOptions.centerCropTransform().centerCrop().placeholder(R.color.app_color_f6).diskCacheStrategy(DiskCacheStrategy.ALL);Glide.with(viewHolder.itemView.getContext()).load(PictureMimeType.isContent(path) && !media.isCut() && !media.isCompressed() ? Uri.parse(path): path).apply(options).into(viewHolder.mImg);}//itemView 的点击事件if (mItemClickListener != null) {viewHolder.itemView.setOnClickListener(v -> {int adapterPosition = viewHolder.getAbsoluteAdapterPosition();mItemClickListener.onItemClick(v, adapterPosition);});}if (mItemLongClickListener != null) {viewHolder.itemView.setOnLongClickListener(v -> {int adapterPosition = viewHolder.getAbsoluteAdapterPosition();mItemLongClickListener.onItemLongClick(viewHolder, adapterPosition, v);return true;});}}}private OnItemClickListener mItemClickListener;public void setOnItemClickListener(OnItemClickListener l) {this.mItemClickListener = l;}public interface OnItemClickListener {/*** Item click event** @param v* @param position*/void onItemClick(View v, int position);/*** Open PictureSelector*/void openPicture();}private OnItemLongClickListener mItemLongClickListener;public void setItemLongClickListener(OnItemLongClickListener l) {this.mItemLongClickListener = l;}
}
4. 布局空间初始化
 FullyGridLayoutManager manager = new FullyGridLayoutManager(mContext, 3, GridLayoutManager.VERTICAL, false);mRecyclerView.setLayoutManager(manager);adapter = new GridImageAdapter(getContext(), mData);adapter.setSelectMax(maxSelectNum);mRecyclerView.setAdapter(adapter);imageEngine = GlideEngine.createGlideEngine();

5. 点击增加弹框布局

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="vertical"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_margin="10dp"android:orientation="vertical"><TextViewandroid:id="@+id/tv_album"android:layout_width="match_parent"android:layout_height="wrap_content"android:background="@drawable/shape_album"android:gravity="center"android:padding="15dp"android:text="相册"android:textSize="16sp"/><Viewandroid:layout_width="match_parent"android:layout_height="1dp"android:background="#f5f5f5"/><TextViewandroid:id="@+id/tv_camera"android:layout_width="match_parent"android:layout_height="wrap_content"android:background="@drawable/shape_camera"android:gravity="center"android:padding="15dp"android:text="拍照"android:textSize="16sp"/><TextViewandroid:id="@+id/tv_cancel"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginTop="10dp"android:background="@drawable/shape_cancel"android:gravity="center"android:padding="15dp"android:text="取消"android:textSize="16sp"/></LinearLayout></LinearLayout>

6. 弹框页面初始化

 View bottomView = View.inflate(mContext, R.layout.layout_bottom_dialog, null);TextView mAlbum = bottomView.findViewById(R.id.tv_album);TextView mCamera = bottomView.findViewById(R.id.tv_camera);TextView mCancel = bottomView.findViewById(R.id.tv_cancel);pop = new PopupWindow(bottomView, -1, -2);pop.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));pop.setOutsideTouchable(true);pop.setFocusable(true);WindowManager.LayoutParams lp = getActivity().getWindow().getAttributes();lp.alpha = 0.5f;getActivity().getWindow().setAttributes(lp);pop.setOnDismissListener(new PopupWindow.OnDismissListener() {@Overridepublic void onDismiss() {WindowManager.LayoutParams lp = getActivity().getWindow().getAttributes();lp.alpha = 1f;getActivity().getWindow().setAttributes(lp);}});pop.setAnimationStyle(R.style.main_menu_photo_anim);pop.showAtLocation(getActivity().getWindow().getDecorView(), Gravity.BOTTOM, 0, 0);

7.  弹框页面监听初始化

View.OnClickListener clickListener = new View.OnClickListener() {@Overridepublic void onClick(View view) {switch (view.getId()) {case R.id.tv_album://相册Log.d("打开相册","sss");PictureSelector.create(GoodItemTitleFragment.this).openGallery(SelectMimeType.ofImage()).setImageEngine(GlideEngine.createGlideEngine()).setMaxSelectNum(maxSelectNum).setMinSelectNum(1).setImageSpanCount(4).forResult(new OnResultCallbackListener<LocalMedia>() {@Overridepublic void onResult(ArrayList<LocalMedia> result) {selectList.addAll(result);//Log.d("ceshi"+RESULT_OK, String.valueOf(images));adapter.setList(selectList);adapter.notifyDataSetChanged();}@Overridepublic void onCancel() {}});/** PictureSelector.create(GoodItemTitleFragment.this).openGallery(PictureMimeType.ofImage()).maxSelectNum(maxSelectNum).minSelectNum(1).imageSpanCount(4).selectionMode(PictureConfig.MULTIPLE).forResult(PictureConfig.CHOOSE_REQUEST);**/break;case R.id.tv_camera://拍照Log.d("打开拍照","sss");PictureSelector.create(GoodItemTitleFragment.this).openCamera(SelectMimeType.ofVideo()).forResultActivity(PictureConfig.REQUEST_CAMERA);/**PictureSelector.create(GoodItemTitleFragment.this).openCamera(PictureMimeType.ofImage()).forResult(PictureConfig.CHOOSE_REQUEST);**/break;case R.id.tv_cancel://取消closePopupWindow();break;}closePopupWindow();}};

8. 增加拍照回调,不加这个图片回调不成功哦。

@SuppressLint("RestrictedApi")@Overridepublic void onActivityResult(int requestCode, int resultCode, Intent data) {super.onActivityResult(requestCode, resultCode, data);Log.d("ceshi"+requestCode,"111");Log.d("ceshi"+resultCode,"222");//Log.d("ceshi"+RESULT_OK,"333");List<LocalMedia> images;if (resultCode == -1) {//Log.d("ceshi"+RESULT_OK,"111");if (requestCode == PictureConfig.REQUEST_CAMERA) {// 图片选择结果回调images = PictureSelector.obtainSelectorList(data);selectList.addAll(images);//Log.d("ceshi"+RESULT_OK, String.valueOf(images));adapter.setList(selectList);adapter.notifyDataSetChanged();}}}

本功能涉及的功能较多,用了几天的时间算集成完。欢迎点赞、转发、首次。

相关文章:

android项目实战之使用框架 集成多图片、视频的上传

效果图 实现方式&#xff0c;本功能使用PictureSelector 第三方库 。作者项目地址&#xff1a;https://github.com/LuckSiege/PictureSelector 1. builder.gradle 增加 implementation io.github.lucksiege:pictureselector:v3.11.1implementation com.tbruyelle.rxpermissio…...

MyBatis查询优化:枚举在条件构建中的妙用

&#x1f680; 作者主页&#xff1a; 有来技术 &#x1f525; 开源项目&#xff1a; youlai-mall &#x1f343; vue3-element-admin &#x1f343; youlai-boot &#x1f33a; 仓库主页&#xff1a; Gitee &#x1f4ab; Github &#x1f4ab; GitCode &#x1f496; 欢迎点赞…...

Isaac Sim教程04 Isaac Sim的高级使用

Isaac Sim 高级使用 版权信息 Copyright 2023 Herman YeAuromix. All rights reserved.This course and all of its associated content, including but not limited to text, images, videos, and any other materials, are protected by copyright law. The author holds…...

《数据结构、算法与应用C++语言描述》-线索二叉树的定义与C++实现

_23Threaded BinaryTree 可编译运行代码见&#xff1a;GIithub::Data-Structures-Algorithms-and-Applications/_24Threaded_BinaryTree 线索二叉树定义 在普通二叉树中&#xff0c;有很多nullptr指针被浪费了&#xff0c;可以将其利用起来。 首先我们要来看看这空指针有多少…...

删除误提交的 git commit

背景描述 某次的意外 commit 中误将密码写到代码中并且 push 到了 remote repo 里面, 本文将围绕这个场景讨论如何弥补. 模拟误提交操作 在 Gitee 创建一个新的 Repo, clone 到本地 git clone https://gitee.com/lpwm/myrepo.git创建两个文件, commit 后 push 到 remote 作…...

机器学习---pySpark案例

1、统计PV&#xff0c;UV 1.if __name__ __main__: 2. conf SparkConf() 3. conf.setMaster("local") 4. conf.setAppName("test") 5. sc SparkContext(confconf) 6. 7. #pv 8. sc.textFile("./pvuv").map(lambda line:(l…...

【链表Linked List】力扣-24 两两交换链表中的节点

目录 题目描述 解题过程 题目描述 给你一个链表&#xff0c;两两交换其中相邻的节点&#xff0c;并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题&#xff08;即&#xff0c;只能进行节点交换&#xff09;。 示例 1&#xff1a; 输入&#xff1a;he…...

企业微信协议开发,API接口调用

产品说明 一、 hook版本&#xff1a;企业微信hook接口是指将企业微信的功能封装成dll&#xff0c;并提供简易的接口给程序调用。通过hook技术&#xff0c;可以在不修改企业微信客户端源代码的情况下&#xff0c;实现对企业微信客户端的功能进行扩展和定制化。企业微信hook接口…...

代码随想录算法训练营 ---第五十五天

今天是 动态规划&#xff1a;编辑距离问题。 第一题&#xff1a; 简介&#xff1a; 动态规划五部曲&#xff1a; 1.确定dp数组的含义 dp[i][j] 表示以下标i-1为结尾的字符串s&#xff0c;和以下标j-1为结尾的字符串t&#xff0c;相同子序列的长度为dp[i][j]。 2.确定递推公…...

【Intel/Altera】 全系列FPGA最新汇总说明,持续更新中

前言 2023年11月14日英特尔 FPGA中国技术日&#xff0c;Intel刚发布了新的FPGA系列&#xff0c;官网信息太多&#xff0c;我这里结合以前的信息&#xff0c;简单汇总更新一下&#xff0c;方便大家快速了解Intel/Altera FPGA家族。 目录 前言 Altera和Intel 型号汇总 1. Agi…...

利用flask将yolov5算法封装成在线推理服务

本脚本主要参考了yolov5工程文件夹下面的detect.py,将yolov5算法封装成了一个在线的推理服务,可以接受app请求,然后推理图片,并将检测结果以json返回,该服务可以供数据标注平台请求。 from flask import * import shutil import json import os import pynvml import pand…...

vue3父子传值实现弹框功能

在Vue3中&#xff0c;我们可以通过 provide 和 inject 来实现父子组件之间的数据传递&#xff0c;这也适用于实现弹框功能。下面是一个简单的例子&#xff1a; 父组件代码&#xff1a; <template><div><button click"showDialog">打开弹框</b…...

C++入门【2-C++ 数据类型】

C 数据类型 使用编程语言进行编程时&#xff0c;需要用到各种变量来存储各种信息。变量保留的是它所存储的值的内存位置。这意味着&#xff0c;当您创建一个变量时&#xff0c;就会在内存中保留一些空间。 您可能需要存储各种数据类型&#xff08;比如字符型、宽字符型、整型…...

按照官网文档 通过useExtendedLib扩展库 引入WeUI,报错 组件未定义 | 解决办法

检查开发者工具版本是否过老 参考博客 不要使用 游客模式&#xff0c;游客模式不支持&#xff0c;请注册Appid 使用。 注意 扩展库方式 和 npm 方式不能同时使用&#xff0c;会有相应报错...

Chat-GPT原理

Chat-GPT原理核心:基于Transformer 架构 ​ 以下是参考文献的部分截图原文说明&#xff1a; ​ Transformers are based on the “attention mechanism,” which allows the model to pay more attention to some inputs than others, regardless of where they show up in t…...

GODOC命令无效,原因是需要手动安装

在看《GO程序设计语言》这本书&#xff0c;按照其中的内容&#xff0c;想看下GO自带的包的文档。 书中讲&#xff0c;可以直接输入GoDOC命令来打开一个服务器&#xff0c;从而可以用浏览器访问文档库。输入命令后&#xff0c;系统提示找不到该命令。 查了资料后才发现&#xff…...

忽略python运行出现的大量警告

添加以下代码即可 import warnings warnings.filterwarnings(ignore)...

【Polar靶场WEB签到】

题目&#xff1a; <?phperror_reporting(0);$file $_GET[file];if(!isset($file))$file 1;$file str_replace(../, , $file);include_once($file.".php");highlight_file(__FILE__); ?>解答&#xff1a;1、进入index页面&#xff0c;说让你加弟弟&#x…...

Linux详解——常用命令(二)

目录 一、常用命令 1.进程相关命令 2.vi命令 3.软件相关命令 RPM命令 YUM命令 4.用户和组相关命令 5.权限相关命令 一、常用命令 1.进程相关命令 # 1.ps 询在当前控制台上运行的进程 ps -aux 说明:查询系统中所有运行的进程&#xff0c;包括后台进程&#xff0c;其…...

TCP首部格式_基本知识

TCP首部格式 表格索引: 源端口目的端口 序号 确认号 数据偏移保留 ACK等 窗口检验和紧急指针 TCP报文段首部格式图 源端口与目的端口: 各占16位 序号:占32比特&#xff0c;取值范围0~232-1。当序号增加到最后一个时&#xff0c;下一个序号又回到0。用来指出本TCP报文段数据载…...

网络六边形受到攻击

大家读完觉得有帮助记得关注和点赞&#xff01;&#xff01;&#xff01; 抽象 现代智能交通系统 &#xff08;ITS&#xff09; 的一个关键要求是能够以安全、可靠和匿名的方式从互联车辆和移动设备收集地理参考数据。Nexagon 协议建立在 IETF 定位器/ID 分离协议 &#xff08;…...

【OSG学习笔记】Day 18: 碰撞检测与物理交互

物理引擎&#xff08;Physics Engine&#xff09; 物理引擎 是一种通过计算机模拟物理规律&#xff08;如力学、碰撞、重力、流体动力学等&#xff09;的软件工具或库。 它的核心目标是在虚拟环境中逼真地模拟物体的运动和交互&#xff0c;广泛应用于 游戏开发、动画制作、虚…...

linux 错误码总结

1,错误码的概念与作用 在Linux系统中,错误码是系统调用或库函数在执行失败时返回的特定数值,用于指示具体的错误类型。这些错误码通过全局变量errno来存储和传递,errno由操作系统维护,保存最近一次发生的错误信息。值得注意的是,errno的值在每次系统调用或函数调用失败时…...

Unity UGUI Button事件流程

场景结构 测试代码 public class TestBtn : MonoBehaviour {void Start(){var btn GetComponent<Button>();btn.onClick.AddListener(OnClick);}private void OnClick(){Debug.Log("666");}}当添加事件时 // 实例化一个ButtonClickedEvent的事件 [Formerl…...

TSN交换机正在重构工业网络,PROFINET和EtherCAT会被取代吗?

在工业自动化持续演进的今天&#xff0c;通信网络的角色正变得愈发关键。 2025年6月6日&#xff0c;为期三天的华南国际工业博览会在深圳国际会展中心&#xff08;宝安&#xff09;圆满落幕。作为国内工业通信领域的技术型企业&#xff0c;光路科技&#xff08;Fiberroad&…...

Spring AI Chat Memory 实战指南:Local 与 JDBC 存储集成

一个面向 Java 开发者的 Sring-Ai 示例工程项目&#xff0c;该项目是一个 Spring AI 快速入门的样例工程项目&#xff0c;旨在通过一些小的案例展示 Spring AI 框架的核心功能和使用方法。 项目采用模块化设计&#xff0c;每个模块都专注于特定的功能领域&#xff0c;便于学习和…...

《Docker》架构

文章目录 架构模式单机架构应用数据分离架构应用服务器集群架构读写分离/主从分离架构冷热分离架构垂直分库架构微服务架构容器编排架构什么是容器&#xff0c;docker&#xff0c;镜像&#xff0c;k8s 架构模式 单机架构 单机架构其实就是应用服务器和单机服务器都部署在同一…...

sshd代码修改banner

sshd服务连接之后会收到字符串&#xff1a; SSH-2.0-OpenSSH_9.5 容易被hacker识别此服务为sshd服务。 是否可以通过修改此banner达到让人无法识别此服务的目的呢&#xff1f; 不能。因为这是写的SSH的协议中的。 也就是协议规定了banner必须这么写。 SSH- 开头&#xff0c…...

qt+vs Generated File下的moc_和ui_文件丢失导致 error LNK2001

qt 5.9.7 vs2013 qt add-in 2.3.2 起因是添加一个新的控件类&#xff0c;直接把源文件拖进VS的项目里&#xff0c;然后VS卡住十秒&#xff0c;然后编译就报一堆 error LNK2001 一看项目的Generated Files下的moc_和ui_文件丢失了一部分&#xff0c;导致编译的时候找不到了。因…...

MySQL基本操作(续)

第3章&#xff1a;MySQL基本操作&#xff08;续&#xff09; 3.3 表操作 表是关系型数据库中存储数据的基本结构&#xff0c;由行和列组成。在MySQL中&#xff0c;表操作包括创建表、查看表结构、修改表和删除表等。本节将详细介绍这些操作。 3.3.1 创建表 在MySQL中&#…...