android高级UI之Canvas综合案例操练

2023-10-30 09:10

本文主要是介绍android高级UI之Canvas综合案例操练,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在上一次https://www.cnblogs.com/webor2006/p/12679470.html对于Canvas的坐标系和Layer进行了学习,这次来看一个关于Canvas的综合案例,对之前的学习加以巩固,其实现也不是很简单,下面一点点来攻克它。

效果演示:

先来看一下最终的效果:

其中这个效果一般会应用于相机APP中,看一个下面从fragmentapp.com【貌似目前该网站打不开了。。】上的一个视频截图效果:

具体实现:

说实话我看到这个效果还是有点为难的,一个图片怎么能根据是否选中和未选中来显示一部分是亮色,一部分是灰色效果呢?要自定义View肯定是不用说了,其实这里面还是蕴藏很多知识点的,里面的API平常也用得比较少,下面就一步步来剖析它的实现。

先来了解Drawable的本质:

对于Drawable我们平常开发中每天都会接触到,很简单嘛它就代表一张“图”,这是从我们使用的角度来说确实是的,但是!!它是一个可画的对象,其可能是一张位图(BitmapDrawable),也有可能是一个图形,一个图层,我们根据画图的需求,创建相应的可画对象,可以理解为一个内存画布,在Drawable中有一个draw()方法:

那为啥先要了解它呢?因为要实现图片的半灰半亮的效果就需要使用自定义Drawable的技法,而它有啥有了解的呢?看下面两个问题:

1、Drawable实例到底是如何被绘制到屏幕上面的?

2、Drawable源码中的那些API方法又是什么时候被谁调用的?

此时肯定要从源码的角度来找答案了,而平常我们在获得Drawable对象时都会这样写:

所以寻找的入口就从它开始,跟进去:

Drawable实例是如何创建的?

继续往下跟:

其中ConstantState是恒定状态的意思,这里对它稍加解释一下:每个Drawable类对象类都关联有一个ConstantState类对象,这是为了保存Drawable类对象的一些恒定不变的数据,如果从同一个res中创建的Drawable类对象,为了节约内存,它们会共享同一个ConstantState类对象,比如一个ColorDrawable类对象,它会关联一个ColorState类对象,color的颜色值是保存在ColorState类对象中的。如果修改ColorDrawable的颜色值,会修改到ColorState的值,会导致和ColorState关联的所有的ColorDrawable的颜色都改变。在修改ColorDrawable的属性的时候,需要先调用public Drawable mutate()方法,让Drawable复制一个新的ConstantState对象关联。

好,继续往下:

Drawable实例如何绘制在屏幕上的?

平常显示图片我们通常会使用ImageView控件,通常会这样使用:

所以此时就从这个入口开始寻找答案:

好!!既然要看如何绘制,对于View来说肯定得瞅onDraw()方法了:

所以能过这个分析,可以看到Drawable并非是一张图片,而是它是用来加载图片的一个工具。

RevealView图形实现:

框架搭建:

咱们首先来实现一个静态图的效果,滑动的先不管,如下:

这里其实是用到了两张图片,素材如下:

所以先将它们导进来:

然后这里自定义一个Drawable,里面会持有这两个Drawable,最终再加上一些逻辑达到最终所看到的一半亮一半灰的效果,如下:

package com.paintgradient.test.canvas;import android.annotation.SuppressLint;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.Log;public class RevealDrawable extends Drawable {private Drawable mUnselectedDrawable;private Drawable mSelectedDrawable;public RevealDrawable(Drawable unselected, Drawable selected) {mUnselectedDrawable = unselected;mSelectedDrawable = selected;}@Overridepublic void draw(Canvas canvas) {}@Overrideprotected void onBoundsChange(Rect bounds) {// 定好两个Drawable图片的宽高---边界boundsmUnselectedDrawable.setBounds(bounds);mSelectedDrawable.setBounds(bounds);Log.d("cexo", "w = " + bounds.width());}@Overridepublic int getIntrinsicWidth() {//得到Drawable的实际宽度return Math.max(mSelectedDrawable.getIntrinsicWidth(),mUnselectedDrawable.getIntrinsicWidth());}@Overridepublic int getIntrinsicHeight() {//得到Drawable的实际高度return Math.max(mSelectedDrawable.getIntrinsicHeight(),mUnselectedDrawable.getIntrinsicHeight());}@Overridepublic void setAlpha(int alpha) {}@Overridepublic void setColorFilter(ColorFilter cf) {}@SuppressLint("WrongConstant")@Overridepublic int getOpacity() {return 0;}}

对于没有自定义过Drawable的对于上面的API其实也一看就明白,定义过一次之后就晓得套路了,然后咱们在布局文件中声明个ImageView:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:gravity="center"tools:context=".MainActivity"><ImageViewandroid:id="@+id/imageView"android:layout_width="wrap_content"android:layout_height="wrap_content" /></LinearLayout>

然后这么来调用:

package com.paintgradient.test;import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.ImageView;import com.paintgradient.test.canvas.RevealDrawable;public class MainActivity extends AppCompatActivity {private ImageView imageView;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);imageView = findViewById(R.id.imageView);RevealDrawable revealDrawable = new RevealDrawable(getDrawable(R.drawable.avft), getDrawable(R.drawable.avft_active));imageView.setImageDrawable(revealDrawable);}
}

思路整理:

好,核心的核心就是如何来编写它里面的draw(Canvas canvas)方法了,其实现思路也比较简单:

 

 

也就实现了我们所要的效果:

Gravity.apply()扣图利器了解:

那么问题来了,如何能从得到的Drawable原图中进行相印的裁剪呢?这里对于Canvas来说有一个这样的API能达到裁剪的目的:

然后再让原图往这个画布上绘制既可实现图片裁剪的效果了,示例代码如下:

那么重点来了,如何来计算这个temp要显示的这个矩形区域呢?这里要用到一个平常可能用得比较少但是面对这种场景来说又非常实现的一个API了:

我猜8成以上搞Android的人应该都没用过,我也没有,所以学完这次之后对于要扣图的场景到时第一时间就可以想到它~~下面先来对它的使用有一个基本的了解,只有对于基础有了掌握对于高大上的效果你才能有比较好的掌控,先来了解四个参数的含义:

  • gravity
    它表示扣图的方向,是从左还是从右,啥意思?拿我们要作案的对象来说:

  • w、h
    它代表目标矩形的宽高,也就是最终要生成的图的宽高,很明显咱们最终扣出来的图就是原图的高度,用灰图或亮图任意一个高宽都可以:
  • container
    它是指被扣的Rect矩形区域,对于咱们来说就是getBounds()返回的矩形区域就成了:

    所以我们可以直接拿来用:

  • outRect
    这个就比较好理解了,最终生成的目标矩形区域,该矩形其实也就是最终要用画面来clipRect()的:

好,下面咱们拿一个图片来做一个试验,从图中扣出我们想要矩形的位置出来,这里以灰色的这张图作为试验品,代码如下:

运行看一下:

 

很明显就是这块位置:

好,我们最终的目标是想从它里面将左半边部分扣出来,其实也就是改变一下高度既可,当然此时宽高就得计算得到:宽度为整个图片的一半,高度则为整个图片的高度,具体修改如下:

运行:

逻辑实现:

好,知道了怎么来扣半图了,接下来则再来将亮图的右半部分扣出来,其我们要的一阴一阳的效果就可以达成了,这个实现就比较简单了,下面看代码:

package com.paintgradient.test.canvas;import android.annotation.SuppressLint;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.Log;
import android.view.Gravity;public class RevealDrawable extends Drawable {private Drawable mUnselectedDrawable;private Drawable mSelectedDrawable;public RevealDrawable(Drawable unselected, Drawable selected) {mUnselectedDrawable = unselected;mSelectedDrawable = selected;}@Overridepublic void draw(Canvas canvas) {Rect containerRect = getBounds();Rect outRect = new Rect();//从一个已有的bound矩形边界范围当中抠出一个我们想要的矩形Gravity.apply(//从左边扣还是从右边扣Gravity.LEFT,//目标矩形宽containerRect.width() / 2,//目标矩形高containerRect.height(),//被扣的地方containerRect,//抠出来outRect);canvas.save();canvas.clipRect(outRect);mUnselectedDrawable.draw(canvas);canvas.restore();Gravity.apply(//从左边扣还是从右边扣Gravity.RIGHT,//目标矩形宽containerRect.width() / 2,//目标矩形高containerRect.height(),//被扣的地方containerRect,outRect);canvas.clipRect(outRect);mSelectedDrawable.draw(canvas);}@Overrideprotected void onBoundsChange(Rect bounds) {// 定好两个Drawable图片的宽高---边界boundsmUnselectedDrawable.setBounds(bounds);mSelectedDrawable.setBounds(bounds);Log.d("cexo", "w = " + bounds.width());}@Overridepublic int getIntrinsicWidth() {//得到Drawable的实际宽度return Math.max(mSelectedDrawable.getIntrinsicWidth(),mUnselectedDrawable.getIntrinsicWidth());}@Overridepublic int getIntrinsicHeight() {//得到Drawable的实际高度return Math.max(mSelectedDrawable.getIntrinsicHeight(),mUnselectedDrawable.getIntrinsicHeight());}@Overridepublic void setAlpha(int alpha) {}@Overridepublic void setColorFilter(ColorFilter cf) {}@SuppressLint("WrongConstant")@Overridepublic int getOpacity() {return 0;}}

其中要特别的注意:

最终就成功实现了一阴一阳的扣图效果了,等于是用二张图来实现的,当然其实也可以用一张图,然后通过滤镜的功能达成一阴一阳,这块可以自行拓展,重点是学会这种实现的思路。

GallaryHorizonalScrollView滑动效果实现:

知道了如何扣图了之后,接下来则就需要依赖这个基本知识实现最终开篇所示的那个滑动效果了。

将所有View添加到HorizontalScrollView中:

这里先将所有用到的资源图都导进来,都是成对的图片,如下:

avft、avft_active:

box_stack、box_stack_active:

bubble_frame、bubble_frame_active:

bubbles、bubbles_active:

bullseye、bullseye_active:

circle_filled、circle_filled_active:

circle_outline、circle_outline_active:

然后声明一下滑动控件:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"tools:context=".MainActivity"><com.paintgradient.test.canvas.GallaryHorizonalScrollViewandroid:id="@+id/hsv"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_gravity="center_vertical"android:background="#AA444444"android:scrollbars="none" />
</LinearLayout>
package com.paintgradient.test;import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.ImageView;import com.paintgradient.test.canvas.GallaryHorizonalScrollView;
import com.paintgradient.test.canvas.RevealDrawable;public class MainActivity extends AppCompatActivity {private ImageView iv;private int[] imgIds = new int[]{ //7个R.drawable.avft,R.drawable.box_stack,R.drawable.bubble_frame,R.drawable.bubbles,R.drawable.bullseye,R.drawable.circle_filled,R.drawable.circle_outline,R.drawable.avft,R.drawable.box_stack,R.drawable.bubble_frame,R.drawable.bubbles,R.drawable.bullseye,R.drawable.circle_filled,R.drawable.circle_outline};private int[] imgIds_active = new int[]{R.drawable.avft_active, R.drawable.box_stack_active, R.drawable.bubble_frame_active,R.drawable.bubbles_active, R.drawable.bullseye_active, R.drawable.circle_filled_active,R.drawable.circle_outline_active,R.drawable.avft_active, R.drawable.box_stack_active, R.drawable.bubble_frame_active,R.drawable.bubbles_active, R.drawable.bullseye_active, R.drawable.circle_filled_active,R.drawable.circle_outline_active};public Drawable[] revealDrawables;private GallaryHorizonalScrollView hzv;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);initData();initView();}private void initData() {revealDrawables = new Drawable[imgIds.length];}private void initView() {for (int i = 0; i < imgIds.length; i++) {RevealDrawable rd = new RevealDrawable(getResources().getDrawable(imgIds[i]),getResources().getDrawable(imgIds_active[i]));revealDrawables[i] = rd;}hzv = findViewById(R.id.hsv);hzv.addImageViews(revealDrawables);}
}
package com.paintgradient.test.canvas;import android.content.Context;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.widget.HorizontalScrollView;
import android.widget.ImageView;
import android.widget.LinearLayout;public class GallaryHorizonalScrollView extends HorizontalScrollView {private static final String TAG = "cexo";private LinearLayout container;public GallaryHorizonalScrollView(Context context, AttributeSet attrs) {super(context, attrs);init();}public GallaryHorizonalScrollView(Context context) {super(context);init();}private void init() {//在ScrollView里面放置一个水平线性布局,里面再放置ImageViewLinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,LinearLayout.LayoutParams.WRAP_CONTENT);container = new LinearLayout(getContext());container.setLayoutParams(params);}//添加图片的方法public void addImageViews(Drawable[] revealDrawables) {for (int i = 0; i < revealDrawables.length; i++) {ImageView img = new ImageView(getContext());img.setImageDrawable(revealDrawables[i]);container.addView(img);}addView(container);}}

此时运行:

可以看到每个ImageView显示的图片都是一阴一阳的,因为目前我们的自定义Drawable的逻辑是写死成这样了:

这里跟原效果还是有一个区别就是进来之后,第一个图片的位置跟左边是有间距的,以及最后一个图片也是有间距的,回忆一下预其的效果:

 

那为啥要设置左右的间距呢?其实也很好理解,因为如果不设置间距,像第一个和最后一个图像是不是没有办法滑到中间呀,预期的效果是滑到中间的则为选中高亮,再来看一下咱们目前的效果:

 

而同样的,对于最后的区域:

所以咱们先来解决这个小细节,其实很好解决,因为整个滑动里面的ImageView都是添加到container父布局中的:

所以我们给这个container的头和尾加个padding不就可以解决了,那加到哪呢?这里加到onLayout就可以了,代码比较简单就直接贴出来了:

所以此时就变成这样了:

ImageView.setImageLevel()理解:

好,接下来则要到烧脑的时间了,那怎么能最终达到我们跟随手机可以滑动其整个的阴暗效果呢?这里需要有一定的技巧的,而且前提需要先理解一个平常不怎么使用的API,也就是如标题所示: 

这官方注解等于没说,完全不理解这方法的含义,先不管啥含义了,先看一下它里面的实现,最终会看到一个新大陆:

而mDrawable就是我们调用这个方法赋值的:

好,接着再跟进去:

而对于我们自定义Drawable来说就可以重写它了:

当然最终要实现我们想要的效果肯定是要用到这个方法的,但是!!现在对于这个方法有啥用还是一脸茫然,此时先读一读官方说明:

读完之后,貌似可以看到可以通过控制这个level的值来达到图片的不断刷新,也就是类似于自定View调用invalidate的效果,不过还是有些抽象,这里由于我们肯定是需要我们的Drawable要不断进行变化,所以先按官方要求重写onLevelChange()方法:

那咱们主动来调用一下setLevel试一下效果:

 

运行:

很明显我们在滑动时是要不断对咱们的Drawable进行图像扣图操作的,此时就可以利用这个特性,通过不断调用setImageLevel()来达到不断更新的目的。

滑动监听来找到渐变左右图的位置:

由于我们需要根据滑动监听来确定当前滑动的图片和那一张图片的位置才能够对其进行相印的扣图逻辑,所以先来处理监听,比如简单:

运行看一下:

而这里先将else中的灰度部分先处理了,这个比较简单,直接将ImageLevel定为0既可:

而在RevealDrawable中的onDraw()中这样写:

@Overridepublic void draw(Canvas canvas) {int level = getLevel();if (level == 0) {mUnselectedDrawable.draw(canvas);} else {Rect containerRect = getBounds();Rect outRect = new Rect();//从一个已有的bound矩形边界范围当中抠出一个我们想要的矩形Gravity.apply(//从左边扣还是从右边扣Gravity.LEFT,//目标矩形宽containerRect.width() / 2,//目标矩形高containerRect.height(),//被扣的地方containerRect,//抠出来outRect);canvas.save();canvas.clipRect(outRect);mUnselectedDrawable.draw(canvas);canvas.restore();Gravity.apply(//从左边扣还是从右边扣Gravity.RIGHT,//目标矩形宽containerRect.width() / 2,//目标矩形高containerRect.height(),//被扣的地方containerRect,outRect);canvas.clipRect(outRect);mSelectedDrawable.draw(canvas);}}

运行:

 

可以看到随着滑动,其没有在相隔的图像之外的会动态都切换成灰色图像了:

计算左右图的Level:

重中之重的问题来了,如何动态根据滑动来处理左右图位置的高亮效果,这个是个很头疼的问题。。 这里就需要一定的技巧了,我们知道ImageLevel的范围值官方也说了,是从0~10000:

所以这里需要用一个假设法,假设这中间的三个图像就是占10000等份:

为啥要定义三个图像呢?因为你根据预期的效果可以发现,其实就是中间三个图在不断的进行变化,而这三个图之外的基本上就是全灰不用做渐变处理的。另外每个图片占5000等份,所以咱们就可以标出三个值来:

可以看到,如果Level是0和10000时,很显然直接变灰就可以了,而如果是5000则直接变彩,所以咱们先把这个逻辑写进来:

接下来则需要根据滑动来动态计算不同图像的Level值了,这里又有点烧脑了,这里先来图解一下思路:

此时重点就是如何计算m了,也就是滑动所占的Level的等份,这个比较简单,一个图片是占5000等份Level,假设图片宽度为w,那图片每像素所占的比率则为5000/w=r,然后我们再用这个r剩以滑出去的距离数就可以求出来,整个计算公式也就搞定了,所以对于左右图片的Level值也就可以确定了,如下:

此时运行看一下:

其中可以看到最终有一个状态就如我们所期望的中间整个变亮了,因为有张图刚好是滑在了5000的位置了:

 

RevealDrawable根据level值进行最终处理:

好,最后就是来处理混合效果了,也就是扣图这块,这里就涉及到是从左扣还是从右扣了,先来用示意图给整清楚思路:

上面这图的思路一定得要理解,不然看代码会比较晕,下面则具体看一下代码,理解了这个方向之后其实也就不难了,代码如下:

package com.paintgradient.test.canvas;import android.annotation.SuppressLint;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.Log;
import android.view.Gravity;public class RevealDrawable extends Drawable {private Drawable mUnselectedDrawable;private Drawable mSelectedDrawable;public RevealDrawable(Drawable unselected, Drawable selected) {mUnselectedDrawable = unselected;mSelectedDrawable = selected;}@Overridepublic void draw(Canvas canvas) {int level = getLevel();if (level == 10000 || level == 0) {//设置灰色mUnselectedDrawable.draw(canvas);} else if (level == 5000) {//设置成彩色mSelectedDrawable.draw(canvas);} else {//这种则为混色Rect containerRect = getBounds();Rect outRect = new Rect();//1.先绘制灰色部分float ratio = (level / 5000f) - 1f;int gravity = ratio < 0 ? Gravity.LEFT : Gravity.RIGHT;//从一个已有的bound矩形边界范围当中抠出一个我们想要的矩形Gravity.apply(//从左边扣还是从右边扣gravity,//目标矩形宽(int) (containerRect.width() * Math.abs(ratio)),//目标矩形高containerRect.height(),//被扣的地方containerRect,//抠出来outRect);canvas.save();canvas.clipRect(outRect);mUnselectedDrawable.draw(canvas);canvas.restore();gravity = ratio < 0 ? Gravity.RIGHT : Gravity.LEFT;//这里的方向跟上面的图刚好相反Gravity.apply(//从左边扣还是从右边扣gravity,//目标矩形宽containerRect.width() - (int) (containerRect.width() * Math.abs(ratio)),//这里需要相减一下//目标矩形高containerRect.height(),//被扣的地方containerRect,outRect);canvas.save();//保存画布canvas.clipRect(outRect);mSelectedDrawable.draw(canvas);canvas.restore();//恢复之前保存的画布}}@Overrideprotected void onBoundsChange(Rect bounds) {// 定好两个Drawable图片的宽高---边界boundsmUnselectedDrawable.setBounds(bounds);mSelectedDrawable.setBounds(bounds);}@Overridepublic int getIntrinsicWidth() {//得到Drawable的实际宽度return Math.max(mSelectedDrawable.getIntrinsicWidth(),mUnselectedDrawable.getIntrinsicWidth());}@Overridepublic int getIntrinsicHeight() {//得到Drawable的实际高度return Math.max(mSelectedDrawable.getIntrinsicHeight(),mUnselectedDrawable.getIntrinsicHeight());}@Overrideprotected boolean onLevelChange(int level) {Log.e("cexo", "RevealDrawable.onLevelChange():" + level);// 当设置level的时候回调---提醒自己重新绘制invalidateSelf();return true;}@Overridepublic void setAlpha(int alpha) {}@Overridepublic void setColorFilter(ColorFilter cf) {}@SuppressLint("WrongConstant")@Overridepublic int getOpacity() {return 0;}}

最后在初始化时应该给第一个ImageView设置一下Level:

至此,终于搞定,真是挺麻烦了~~通过这个案例对于如何扣图以及ImageLevel的使用就比较熟悉了。

这篇关于android高级UI之Canvas综合案例操练的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/306870

相关文章

python panda库从基础到高级操作分析

《pythonpanda库从基础到高级操作分析》本文介绍了Pandas库的核心功能,包括处理结构化数据的Series和DataFrame数据结构,数据读取、清洗、分组聚合、合并、时间序列分析及大数据... 目录1. Pandas 概述2. 基本操作:数据读取与查看3. 索引操作:精准定位数据4. Group

Android Paging 分页加载库使用实践

《AndroidPaging分页加载库使用实践》AndroidPaging库是Jetpack组件的一部分,它提供了一套完整的解决方案来处理大型数据集的分页加载,本文将深入探讨Paging库... 目录前言一、Paging 库概述二、Paging 3 核心组件1. PagingSource2. Pager3.

RabbitMQ消费端单线程与多线程案例讲解

《RabbitMQ消费端单线程与多线程案例讲解》文章解析RabbitMQ消费端单线程与多线程处理机制,说明concurrency控制消费者数量,max-concurrency控制最大线程数,prefe... 目录 一、基础概念详细解释:举个例子:✅ 单消费者 + 单线程消费❌ 单消费者 + 多线程消费❌ 多

WinForm跨线程访问UI及UI卡死的解决方案

《WinForm跨线程访问UI及UI卡死的解决方案》在WinForm开发过程中,跨线程访问UI控件和界面卡死是常见的技术难题,由于Windows窗体应用程序的UI控件默认只能在主线程(UI线程)上操作... 目录前言正文案例1:直接线程操作(无UI访问)案例2:BeginInvoke访问UI(错误用法)案例

MySql基本查询之表的增删查改+聚合函数案例详解

《MySql基本查询之表的增删查改+聚合函数案例详解》本文详解SQL的CURD操作INSERT用于数据插入(单行/多行及冲突处理),SELECT实现数据检索(列选择、条件过滤、排序分页),UPDATE... 目录一、Create1.1 单行数据 + 全列插入1.2 多行数据 + 指定列插入1.3 插入否则更

Python通用唯一标识符模块uuid使用案例详解

《Python通用唯一标识符模块uuid使用案例详解》Pythonuuid模块用于生成128位全局唯一标识符,支持UUID1-5版本,适用于分布式系统、数据库主键等场景,需注意隐私、碰撞概率及存储优... 目录简介核心功能1. UUID版本2. UUID属性3. 命名空间使用场景1. 生成唯一标识符2. 数

Android kotlin中 Channel 和 Flow 的区别和选择使用场景分析

《Androidkotlin中Channel和Flow的区别和选择使用场景分析》Kotlin协程中,Flow是冷数据流,按需触发,适合响应式数据处理;Channel是热数据流,持续发送,支持... 目录一、基本概念界定FlowChannel二、核心特性对比数据生产触发条件生产与消费的关系背压处理机制生命周期

Android ClassLoader加载机制详解

《AndroidClassLoader加载机制详解》Android的ClassLoader负责加载.dex文件,基于双亲委派模型,支持热修复和插件化,需注意类冲突、内存泄漏和兼容性问题,本文给大家介... 目录一、ClassLoader概述1.1 类加载的基本概念1.2 android与Java Class

PostgreSQL的扩展dict_int应用案例解析

《PostgreSQL的扩展dict_int应用案例解析》dict_int扩展为PostgreSQL提供了专业的整数文本处理能力,特别适合需要精确处理数字内容的搜索场景,本文给大家介绍PostgreS... 目录PostgreSQL的扩展dict_int一、扩展概述二、核心功能三、安装与启用四、字典配置方法

Python中你不知道的gzip高级用法分享

《Python中你不知道的gzip高级用法分享》在当今大数据时代,数据存储和传输成本已成为每个开发者必须考虑的问题,Python内置的gzip模块提供了一种简单高效的解决方案,下面小编就来和大家详细讲... 目录前言:为什么数据压缩如此重要1. gzip 模块基础介绍2. 基本压缩与解压缩操作2.1 压缩文