重铸安卓荣光——上传图片组件

痛点:

公司打算做安卓软件,最近在研究安卓,打算先绘制样式

研究发现安卓并不像前端有那么多组件库,甚至有些基础的组件都需要自己实现,记录一下自己实现的组件

成品展示

一个上传图片的组件

  1. 可以选择拍照或者从相册中上传

  2. 上传可以限制数量

  3. 上传后可以选择某张图片删除

动画

引入依赖

build.gradle中引入以下依赖

//图片选择器
implementation("com.github.wildma:PictureSelector:2.1.0")
//照片查看器,可以放大缩小照片
implementation("com.github.chrisbanes:PhotoView:2.3.0")
//自动换行的layout,帮助实现达到宽度后自动下一行
implementation("com.google.android:flexbox:2.0.1")

使用

使用只需要xml中添加该组件即可,其中app开头的属性都是为了让他可以自动换行

    <com.example.androidtest.test.UploadLayoutandroid:id="@+id/uploadLayout"android:layout_width="match_parent"android:layout_height="wrap_content"app:flexWrap="wrap"app:alignItems="stretch"app:alignContent="stretch"app:justifyContent="flex_start"/>

初始化

创建UploadLayout继承FlexboxLayout

继承FlexboxLayout可以实现自动换行,当我们插入的图片占满一行后,会自己换行,如下所示

image-20240220160045978

加载添加按钮

初始化时需要把灰色的添加按钮加载进来

限制最大照片数

maxImage属性用来限制最大上传照片数量

public class UploadLayout extends FlexboxLayout {//最大上传图片数量private Integer maxImage = -1;private TextView uploadPhotoTextView;public UploadLayout(Context context) {super(context);init();}public UploadLayout(Context context, AttributeSet attrs) {super(context, attrs);TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.UploadLayout);maxImage = a.getInteger(R.styleable.UploadLayout_maxImage, -1);a.recycle();init();}public UploadLayout(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init();}private void init() {// 创建一个新的 TextView 控件uploadPhotoTextView = new TextView(getContext());// 设置控件的属性int weight = (int) UiUtils.dp2px(getContext(),80);uploadPhotoTextView.setId(View.generateViewId()); // 生成一个唯一的 IDuploadPhotoTextView.setWidth(weight); // 设置宽度为 80dpuploadPhotoTextView.setHeight(weight); // 设置高度为 80dpuploadPhotoTextView.setGravity(Gravity.CENTER); // 设置文本居中对齐uploadPhotoTextView.setBackground(ContextCompat.getDrawable(getContext(), R.color.viewfinder_text_color4)); // 设置背景颜色uploadPhotoTextView.setText("+"); // 设置文本内容为 "+"uploadPhotoTextView.setTextSize(30); // 设置文本大小为 30dpuploadPhotoTextView.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {// 在这里添加点击事件的逻辑uploadPhoto(v);}});// 设置控件的布局参数,可以根据需要设置边距等LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(weight,weight);layoutParams.setMargins(0, 0, 10, 10); // 设置右边距为 10dp,底边距为 10dpuploadPhotoTextView.setLayoutParams(layoutParams);// 将 TextView 添加到父容器中addView(uploadPhotoTextView);}
}

创建控件时读取参数

在xml中直接使用时,走的是以下构造方法

    public UploadLayout(Context context, AttributeSet attrs) {super(context, attrs);TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.UploadLayout);maxImage = a.getInteger(R.styleable.UploadLayout_maxImage, -1);a.recycle();init();}

限制最大上传数

    <com.example.androidtest.test.UploadLayoutandroid:id="@+id/uploadLayout"android:layout_width="match_parent"android:layout_height="wrap_content"app:flexWrap="wrap"app:alignItems="stretch"app:alignContent="stretch"app:justifyContent="flex_start"app:maxImage="2"/>

需要在res/values/attrs.xml中添加以下代码,让他能读取到maxImage

    <declare-styleable name="UploadLayout"><attr name="maxImage" format="integer" /></declare-styleable>

添加点击事件

selectPicture设置false表示不需要裁剪,如果需要裁剪可以设置为true,测试时报错,好像原因是手机没有照片裁剪器

    public void uploadPhoto(View view){Activity activity = (Activity) view.getContext();//判断数量是否已经达到上线int imageViewCount = 0;for (int i = 0; i < getChildCount(); i++) {View childView = getChildAt(i);if (childView instanceof RelativeLayout) {imageViewCount++;}}if (imageViewCount == maxImage) {//达到上限Toast.makeText(getContext(), "图片上传已达上限", Toast.LENGTH_SHORT).show();return;}//打开照片选择器,PictureSelector.create(activity, PictureSelector.SELECT_REQUEST_CODE).selectPicture(false);}

上传照片回调

我的做法是在uploadLayout中依次插入RelativeLayout

RelativeLayoutImageView用来显示图片,View用来显示删除框

点击查看大图,使用PhotoView可以拖拽放大缩小

public void handleActivityResult(int requestCode, int resultCode, Intent data) {if (requestCode == PictureSelector.SELECT_REQUEST_CODE) {if (data != null) {PictureBean pictureBean = data.getParcelableExtra(PictureSelector.PICTURE_RESULT);RelativeLayout relativeLayout = new RelativeLayout(getContext());ImageView imageView = new ImageView(getContext());int uploadIndex  = this.indexOfChild(uploadPhotoTextView);//设置大小int weight80 = (int) UiUtils.dp2px(getContext(),80);int weight20 = (int) UiUtils.dp2px(getContext(),20);// 设置布局参数FlexboxLayout.LayoutParams layoutParams = new FlexboxLayout.LayoutParams(weight80,weight80); layoutParams.setMargins(0,0, 10,  10);layoutParams.setOrder(uploadIndex);relativeLayout.setLayoutParams(layoutParams);relativeLayout.addView(imageView);if (pictureBean.isCut()) {imageView.setImageBitmap(BitmapFactory.decodeFile(pictureBean.getPath()));} else {imageView.setImageURI(pictureBean.getUri());}//删除按钮View closeView = new View(getContext());closeView.setBackgroundResource(R.drawable.cross_shape);RelativeLayout.LayoutParams closeParams = new RelativeLayout.LayoutParams(weight20,weight20);// 设置ImageView在RelativeLayout中的位置,这里设置为右上角closeParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);closeParams.addRule(RelativeLayout.ALIGN_PARENT_TOP);closeView.setLayoutParams(closeParams);relativeLayout.addView(closeView);//使用 Glide 加载图片Glide.with(this).load(pictureBean.isCut() ? pictureBean.getPath() : pictureBean.getUri()).apply(RequestOptions.centerCropTransform()).into(imageView);//图片点击大图imageView.setOnClickListener(v -> {// 创建一个 Dialog 来显示大图Dialog dialog = new Dialog(getContext(), android.R.style.Theme_Black_NoTitleBar_Fullscreen);dialog.setContentView(R.layout.dialog_image_preview);PhotoView photoView = dialog.findViewById(R.id.photoView);// 使用 Glide 加载大图到 PhotoViewGlide.with(this).load(pictureBean.isCut() ? pictureBean.getPath() : pictureBean.getUri()) // 替换为您的大图 URL.into(photoView);// 点击大图时关闭 DialogphotoView.setOnClickListener(vm -> dialog.dismiss());dialog.show();});//删除closeView.setOnClickListener(v -> {androidx.appcompat.app.AlertDialog.Builder builder = new AlertDialog.Builder(v.getContext());builder.setView(R.layout.dialog_delete_img);builder.setCancelable(false);//能否被取消AlertDialog dialog = builder.create();dialog.show();View cancel = dialog.findViewById(R.id.delete_img_cancel);View commit = dialog.findViewById(R.id.delete_img_commit);cancel.setOnClickListener(v1 -> dialog.dismiss());commit.setOnClickListener(v1 -> {RelativeLayout parentRelativeLayout= (RelativeLayout) closeView.getParent();if (parentRelativeLayout != null) {removeView(parentRelativeLayout);}dialog.dismiss();});});addView(relativeLayout);}}}

删除弹窗:dialog_delete_img.xml

    <RelativeLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:padding="15dp"><TextViewandroid:id="@+id/title"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="确定要删除这张照片吗?"android:textColor="@color/black"android:textSize="15dp"android:layout_centerHorizontal="true"android:layout_marginBottom="30dp"/><RelativeLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_below="@+id/title"android:layout_marginHorizontal="30dp"><Buttonandroid:id="@+id/delete_img_cancel"android:layout_width="120dp"android:layout_height="40dp"android:text="取消"android:textColor="@color/button_orange"android:textSize="15dp"android:layout_alignParentLeft="true"android:background="@drawable/tab_layout_item3"/><Buttonandroid:id="@+id/delete_img_commit"android:layout_width="120dp"android:layout_height="40dp"android:text="确定"android:textColor="@color/white"android:textSize="15dp"android:layout_alignParentRight="true"android:background="@drawable/tab_layout_item4"/></RelativeLayout></RelativeLayout>

删除图案:cross_shape.xml

<?xml version="1.0" encoding="utf-8"?>
<!-- cross_shape.xml -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"><!-- 背景色 --><item><shape android:shape="rectangle"><solid android:color="#80000000" /></shape></item><item><rotateandroid:fromDegrees="45"android:toDegrees="45"><shape android:shape="line"><stroke android:width="1dp" android:color="#FFFFFF" /></shape></rotate></item><item><rotateandroid:fromDegrees="135"android:toDegrees="135"><shape android:shape="line"><stroke android:width="1dp" android:color="#FFFFFF" /></shape></rotate></item></layer-list>

重写Activity方法

在调用的页面的需要重写onActivityResult(),执行咱们的回调函数

public class MainActivity extends AppCompatActivity {private UploadLayout uploadLayout;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);uploadLayout = findViewById(R.id.uploadLayout);}@Overrideprotected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {super.onActivityResult(requestCode, resultCode, data);uploadLayout.handleActivityResult(requestCode,resultCode,data);}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/691714.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

75.SpringMVC的拦截器和过滤器有什么区别?执行顺序?

75.SpringMVC的拦截器和过滤器有什么区别&#xff1f;执行顺序&#xff1f; 区别 拦截器不依赖与servlet容器&#xff0c;过滤器依赖与servlet容器。拦截器只能对action请求(DispatcherServlet 映射的请求)起作用&#xff0c;而过滤器则可以对几乎所有的请求起作用。拦截器可…

【微服务安全】OpenID Connect 简介:现代应用程序的身份验证

OpenID Connect (OIDC) 是一个建立在 OAuth 2.0 之上的开放身份验证协议。它简化了应用程序以一种标准化和可互操作的方式验证用户身份并获取其基本个人资料信息的方式。可以将其视为应用程序“知道你是谁”的一种安全方式&#xff0c;而无需你创建单独的帐户或透露你的密码。 …

压缩感知——革新数据采集的科学魔法

引言&#xff1a; 在数字时代&#xff0c;数据以及数据的收集和处理无处不在。压缩感知(Compressed Sensing, CS)是一种新兴的数学框架&#xff0c;它挑战了我们传统上对数据采集和压缩的看法&#xff0c;给医学图像、天文观测、环境监测等领域带来了颠覆性的影响。但到底什么…

Java 爬虫 jvppeteer

开源地址&#xff1a;https://gitee.com/fanyong920/jvppeteer maven依赖&#xff1a; <!-- java爬虫 --><dependency><groupId>io.github.fanyong920</groupId><artifactId>jvppeteer</artifactId><version>1.1.3</version>…

Leetcode日记 226. 翻转二叉树 给你一棵二叉树的根节点 root ,翻转这棵二叉树,并返回其根节点。

Leetcode日记 226. 翻转二叉树 给你一棵二叉树的根节点 root &#xff0c;翻转这棵二叉树&#xff0c;并返回其根节点。 二叉树简介二叉树分类解题方法制作不易&#xff0c;感谢三连&#xff0c;谢谢啦 二叉树简介 二叉树&#xff08;Binary Tree&#xff09;是一种特殊的树形…

AUTOSAR AP——IDSM 入侵检测管理

什么是入侵检测系统管理器? 入侵检测系统(IDS)是一种安全控制,它检测并处理安全事件。入侵检测系统管理器(IdsM)是自适应平台架构中的一个功能集群,负责处理由安全传感器(自适应应用程序)报告的安全事件。 作为一个功能集群,IdsM包括一个向应用程序提供C++ API的库…

uniapp 放大中间图标

方法1&#xff1a;使用css /*样式直接复制到项目的App.vue即可*/.uni-tabbar { /*.uni-tabbar__item:nth-last-child(3) 修改倒数第三个 也就是中间的图标 我这边底部栏是五个*/.uni-tabbar__item:nth-last-child(3) {.uni-tabbar__bd {/*.uni-tabbar__icon 去掉原图标大小&…

【Spring】循环依赖

目录标题 什么是循环依赖循环依赖场景Java SE 演示Spring 容器演示三级缓存核心知识三级缓存四大方法三级缓存中的迁移 三级缓存源码分析源码思维导图 源码图例课前问题推荐阅读 循环依赖是什么&#xff1f;循环依赖的场景有哪一些&#xff1f;三级缓存分别是什么&#xff1f;三…

openEuler学习——mysql(第一次总结)

1、openEuler 二进制方式安装MySQL 8.0.x。 思路是先从官网获取安装包链接如下https://mirrors.aliyun.com/mysql/MySQL-8.0/mysql-8.0.28-linux-glibc2.12-x86_64.tar.xz 然后解压安装修改权限&#xff0c;可以参考mysql官方网站步骤 [rootopenEuler-node1 ~]# wget -c https:…

DS:循环队列的实现

创作不易&#xff0c;给个三连吧&#xff01;&#xff01; 一、前言 对于循环队列&#xff0c;博主也是源自于一道力扣的OJ题 力扣&#xff1a;循环队列的设置 后来我在网上查过&#xff0c;这个循环队列是有自己的应用场景的&#xff01;&#xff01;并不是出题者为了出题…

网络防火墙综合实验

备注&#xff1a;电信网段15.1.1.0 移动网段14.1.1.0 办公区 11.1.1.0 生产区 10.1.1.0 服务区 13.1.1.0 公网 1.1.1.1 和 2.2.2.2 需求&#xff1a; 1、办公区设备可以通过电信链路和移动链路上网&#xff08;多对多nat&#xff0c;并且需要保留一个公网ip&#xff09; 2、…

excel如何指定求和

在Excel中&#xff0c;你可以使用函数来实现动态求和&#xff0c;使得当指定行的数值更新后&#xff0c;和也随之更新。具体来说&#xff0c;你可以使用SUM函数结合一些动态的引用方法。以下是一种实现方式&#xff1a; 假设你要对A列&#xff08;从A1到A10&#xff0c;以示例…

<网络安全>《40 网络攻防专业课<第六课 - 木马与防范>》

1 木马 1.1 木马简介 木马是攻击者编写的一段恶意代码&#xff0c;它潜伏在被攻击者的计算机中。攻击者通过这个代码可远程控制被攻击者的计算机&#xff0c;以窃取计算机上的信息或者操作计算机。从本质上讲&#xff0c;木马也是病毒的一种&#xff0c;因此不少用户也把木马…

代码随想录算法训练营DAY20 | 二叉树 (8)

一、LeetCode 701 二叉搜索树中的插入操作 题目链接&#xff1a; 701.二叉搜索树中的插入操作https://leetcode.cn/problems/insert-into-a-binary-search-tree/description/ 思路&#xff1a;见缝插针罢辽。 class Solution {public TreeNode insertIntoBST(TreeNode root, i…

ChatGPT-01 用ChatGPT指令,自学任何领域的系统知识

1. 指令位置 Github仓库&#xff1a;Mr Ranedeer AI Tutor 但是需要开通chatgtp plus版本&#xff0c;并且打开代码解释器 2 使用 学习内容 开始学习 GPT甚至可以给你思考题&#xff0c;给出的答案还能进行评价 配置 通过配置表修改 深度 学习风格 沟通风格 语气风格 …

花费200元,我用全志H616和雪糕棒手搓了一台可UI交互的视觉循迹小车

常见的视觉循迹小车都具备有路径识别、轨迹跟踪、转向避障、自主决策等基本功能&#xff0c;如果不采用红外避障的方案&#xff0c;那么想要完全满足以上这些功能&#xff0c;摄像头、电机、传感器这类关键部件缺一不可&#xff0c;由此一来小车成本也就难以控制了。 但如果&a…

WebLogic:常用超时配置

1.连接超时 weblogic.management.configuration.KernelMBean.ConnectTimeout 官方解释&#xff1a; The amount of time that this server should wait to establish an outbound socket connection before timing out. 这个应该是WebLogic作为客户端建立socket连接时。也可使…

深度学习基础之《TensorFlow框架(3)—TensorBoard》

一、TensorBoard可视化学习 1、TensorFlow有一个亮点就是&#xff0c;我们能看到自己写的程序的可视化效果&#xff0c;这个功能就是TensorBoard 2、TensorFlow可用于训练大规模深度神经网络所需的计算&#xff0c;使用该工具涉及的计算往往复杂而深奥。为了方便TensorFlow程…

【代码库】去除字符串中的 HTML 标签

去除字符串中的 HTML 标签内容&#xff0c;只保留文本内容。 import java.util.regex.*;/*** 去除字符串中的 HTML 标签内容&#xff0c;只保留文本内容。*/ public class RemoveHtmlTags {public static void main(String[] args) {// 原始 HTML 字符串String html "<…

C++11---(2)

目录 一、新增容器 1.1、array 1.2、forward_list 1.3、unordered系列 二、右值引用和移动语义 2.1、什么是左值&#xff0c;什么是左值引用 2.2、什么是右值&#xff0c;什么是右值引用 2.3、左值引用和右值引用比较 2.4、右值引用使用场景和意义 2.5、右值引用引用…