工具类汇总(更新ing)

前言

Android工具类是一些封装好的工具方法的集合,用于简化Android开发中的常见操作和实现特定功能。这些工具类可以帮助开发者更高效地编写代码、提高开发效率和减少重复劳动。

一、Intent

1. 获取应用程序信息

(1)从 Intent 或者 URI 中读取应用信息

从 Intent 或者 URI 中读取应用信息APK 包的名称、大小、图标等信息是比较困难的,因为 Intent 或者 URI 并没有直接提供这些信息。不过,你可以通过以下步骤来获取这些信息:

  • 使用包名获取应用程序的 ApplicationInfo 对象。
  • 使用 PackageManager 对象获取应用程序的名称、大小、图标等信息。
    以下是一种可能的实现方式:
// 从 Intent 中获取 URI
Uri uri = intent.getData();
if (uri == null) {
    return;
}

// 从 URI 中获取包名
String packageName = uri.getAuthority();
if (packageName == null) {
    return;
}

PackageManager pm = getPackageManager();
ApplicationInfo appInfo;
try {
    // 使用包名获取 ApplicationInfo 对象
    appInfo = pm.getApplicationInfo(packageName, 0);
} catch (PackageManager.NameNotFoundException e) {
    e.printStackTrace();
    return;
}

// 获取应用名称
String appName = pm.getApplicationLabel(appInfo).toString();

// 获取应用图标
Drawable icon = pm.getApplicationIcon(appInfo);

// 获取应用 APK 文件的路径
String apkPath = appInfo.sourceDir;

// 获取应用 APK 文件的大小
long apkSize = new File(apkPath).length();

需要注意的是,上述代码仅适用于从 URI 中获取包名的情况。如果你从 Intent 中获取了包名,可以将其替换到代码中的 packageName 变量中。此外,如果 URI 中不包含包名,你需要使用其他方式获取包名,例如解析 URI 中的路径信息。

(2)常规使用 PackageManager 类来获取应用信息

以下是一种示例代码:


PackageManager pm = getPackageManager();
try {
    ApplicationInfo appInfo = pm.getApplicationInfo(packageName, 0);

    // 获取应用名称
    String appName = pm.getApplicationLabel(appInfo).toString();

    // 获取应用图标
    Drawable icon = pm.getApplicationIcon(appInfo);

    // 获取应用 APK 文件的路径
    String apkPath = appInfo.sourceDir;

    // 获取应用 APK 文件的大小
    long apkSize = new File(apkPath).length();

} catch (PackageManager.NameNotFoundException e) {
    e.printStackTrace();
}

在上述代码中,我们首先创建一个 PackageManager 对象,然后使用 getApplicationInfo(packageName, 0) 方法获取指定包名对应的应用信息。接着,我们使用 getApplicationLabel(appInfo) 方法获取应用名称,使用 getApplicationIcon(appInfo) 方法获取应用图标。最后,我们使用 appInfo.sourceDir 获取应用 APK 文件的路径,使用 new File(apkPath).length() 获取应用 APK 文件的大小。 需要注意的是,如果指定的包名对应的应用不存在,getPackageManager().getApplicationInfo(packageName, 0) 方法会抛出 PackageManager.NameNotFoundException 异常。在代码中,我们使用 try-catch 块处理该异常。

2、获取Intent携带的uri

(1)获取Uri内容

如果需要兼容其他类型的 Intent,可以在代码中添加相应的处理逻辑。一般来说,可以使用 getData() 方法获取 URI,或者根据 Intent 中的数据类型进行处理。
以下是一种通用的方式,用于兼容多种类型的 Intent:


Uri uri = null;
if (intent != null) {
    String action = intent.getAction();
    if (Intent.ACTION_VIEW.equals(action)) {
        // 获取 VIEW Intent 中的 URI
        uri = intent.getData();
    } else if (Intent.ACTION_SEND.equals(action)) {
        // 获取 SEND Intent 中的 URI
        uri = intent.getParcelableExtra(Intent.EXTRA_STREAM);
    } else if (Intent.ACTION_SEND_MULTIPLE.equals(action)) {
        // 获取 SEND_MULTIPLE Intent 中的 URI
        ArrayList<Uri> uris = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
        if (uris != null && !uris.isEmpty()) {
            uri = uris.get(0);
        }
    } else if (Intent.ACTION_CHOOSER.equals(action)) {
        // 获取 CHOOSER Intent 中的 URI
        Intent chooserIntent = Intent.createChooser(intent, "Select");
        if (chooserIntent != null) {
            uri = chooserIntent.getParcelableExtra(Intent.EXTRA_STREAM);
        }
    } else {
        // 其他情况,根据数据类型获取 URI
        uri = intent.getData();
        if (uri == null) {
            String type = intent.getType();
            if (type != null) {
                // 根据数据类型获取 URI
                String[] types = type.split("/");
                if (types.length == 2) {
                    String dataType = types[0];
                    String dataString = intent.getStringExtra(Intent.EXTRA_TEXT);
                    if (dataString != null) {
                        uri = Uri.parse(dataString);
                    }
                }
            }
        }
    }
}

在上述代码中,我们使用 getData() 方法获取 URI,并在其为空的情况下,根据数据类型进行处理。具体地,我们使用 getType() 方法获取数据类型,然后根据类型进行处理。 在代码中,我们假设数据类型为 text/plain,并使用 getStringExtra() 方法获取 EXTRA_TEXT 键对应的字符串。如果字符串不为空,则将其转换为 URI。如果需要兼容其他类型的数据,可以根据实际情况进行扩展。

(3)获取uri数量

在 Intent 中包含多个文件的 URI 的情况下,可以使用 getClipData() 方法获取 ClipData 对象,然后使用 getItemCount() 方法获取 URI 的数量。 以下是一种获取多个文件 URI 数量的方式:

int uriCount = 0;
if (intent != null) {
    ClipData clipData = intent.getClipData();
    if (clipData != null) {
        uriCount = clipData.getItemCount();
    } else {
        Uri uri = intent.getData();
        if (uri != null) {
            uriCount = 1;
        }
    }
}

在上述代码中,我们首先使用 getClipData() 方法获取 ClipData 对象。如果 ClipData 不为空,则使用 getItemCount() 方法获取 URI 的数量;否则,使用 getData() 方法获取单个 URI,如果 URI 不为空,则数量为 1。 需要注意的是,这种方式只适用于 ACTION_SENDACTION_SEND_MULTIPLE 类型的 Intent,如果 Intent 的类型不是这两种类型,则无法获取多个文件 URI。

三、ImageView

1、描边圆角效果叠加

可以通过创建一个带有描边的 Shape Drawable,然后将其设置为 ImageView 的背景来实现在 ImageView 中设置黑色描边和圆角的效果。具体步骤如下:

(1) 创建一个圆角矩形的 Shape Drawable,并设置其圆角半径。

示例代码如下:

<shape xmlns:android="http://schemas.android.com/apk/res/android"
        android:shape="rectangle">
    
    <corners android:radius="10dp" />
    
</shape>

(2)创建一个带有描边的 Shape Drawable,并将圆角矩形的 Shape Drawable 设置为其内部的 Shape Drawable

示例代码如下:

<shape xmlns:android="http://schemas.android.com/apk/res/android"
        android:shape="rectangle">
    
    <solid android:color="@android:color/transparent" />
    <stroke android:width="1dp" android:color="#000000" />
    <corners android:radius="10dp" />
    
    <padding android:left="1dp" android:top="1dp" android:right="1dp" android:bottom="1dp" />
    
</shape>

上述代码中,使用 stroke 元素来设置描边的宽度和颜色,使用 padding 元素来设置描边的偏移量,这里设置为 1dp。同时,使用 solid 元素将背景颜色设置为透明。将圆角矩形的 Shape Drawable 设置为其内部的 Shape Drawable。

(3)在代码中获取 ImageView 对象,并将带有描边的 Shape Drawable 设置为其背景

示例代码如下:

ImageView imageView = findViewById(R.id.imageView);
Drawable drawable = getResources().getDrawable(R.drawable.rounded_corners_with_stroke);
imageView.setBackground(drawable);

上述代码中,通过 getResources().getDrawable() 方法获取带有描边的 Shape Drawable,并将其设置为 ImageView 的背景。 需要注意的是,如果图片的宽度或高度小于圆角半径的两倍,则圆角的效果可能不明显,因此应该根据具体情况适当调整圆角半径。

2、仅在代码中设置图片圆角

可以通过代码实现在 ImageView 中设置圆角。

具体步骤如下:

  1. 创建一个 Bitmap 对象,并将图片资源转换为 Bitmap 对象。示例代码如下:
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.image);
  1. 创建一个圆角矩形的 ShapeDrawable 对象,并设置其圆角半径。示例代码如下:
float radius = 10f; // 圆角半径
ShapeDrawable shapeDrawable = new ShapeDrawable(new RoundRectShape(new float[] {radius, radius, radius, radius, radius, radius, radius, radius}, null, null));

上述代码中,通过 RoundRectShape 的构造函数设置圆角矩形的圆角半径,其中第一个参数是一个 float 数组,指定了圆角的半径,按照左上、右上、右下、左下的顺序排列,如果某个角不需要圆角则将其半径设置为 0;第二个和第三个参数分别指定了外边框和内边框的矩形,这里不需要设置,因此传入 null。

  1. 创建一个 BitmapShader 对象,并将其设置为 ShapeDrawable 的 Shader。示例代码如下:
BitmapShader bitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
shapeDrawable.getPaint().setShader(bitmapShader);

上述代码中,通过 BitmapShader 的构造函数创建一个 BitmapShader 对象,并将其设置为 ShapeDrawable 的 Shader,以实现将 Bitmap 显示到 ShapeDrawable 中。

  1. 将 ShapeDrawable 设置为 ImageView 的背景。示例代码如下:
imageView.setBackground(shapeDrawable);

上述代码中,通过 setBackground() 方法将 ShapeDrawable 设置为 ImageView 的背景,从而实现在 ImageView 中设置圆角的效果。 完整的代码示例:

Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.image);
float radius = 10f; // 圆角半径
ShapeDrawable shapeDrawable = new ShapeDrawable(new RoundRectShape(new float[] {radius, radius, radius, radius, radius, radius, radius, radius}, null, null));
BitmapShader bitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
shapeDrawable.getPaint().setShader(bitmapShader);
imageView.setBackground(shapeDrawable);

3、AnimationDrawable点击失效解决

AnimationDrawable 是一个逐帧动画,它可以使用一组 Drawable 对象来创建动画的效果。当使用 AnimationDrawableImageView 上播放逐帧动画时,如果给 ImageView 设置了点击事件,你可能会发现点击事件不生效。这是因为在播放动画的过程中,ImageView 实际上是被替换成了一个 Drawable,而不是一个普通的视图。因此,点击事件会被 Drawable 拦截,而不会传递给 ImageView

为了解决这个问题,我们可以在 ImageView 上添加一个覆盖层,来接收点击事件。具体而言,我们可以在 ImageView 的父容器上添加一个透明的视图,然后给这个视图设置点击事件。例如:

<RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

    <ImageView
        android:id="@+id/image_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:src="@drawable/my_animation" />

    <View
        android:id="@+id/click_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@android:color/transparent" />

</RelativeLayout>

在这个示例中,我们在 ImageView 的父容器上添加了一个透明的 View,并将其覆盖在 ImageView 上。然后,我们可以给这个 View 设置点击事件,来接收用户的点击操作。 接下来,我们需要在代码中获取 ImageViewView 对象,并分别设置点击事件。例如:

ImageView imageView = findViewById(R.id.image_view);
View clickView = findViewById(R.id.click_view);

clickView.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        // 处理点击事件
    }
});

在这个示例中,我们使用 findViewById() 方法获取 ImageViewView 对象,并给 View 设置了点击事件。当用户点击 ImageView 时,点击事件会被 View 拦截,并传递给 OnClickListener 处理。

需要注意的是,在使用 AnimationDrawable 播放逐帧动画时,也可以通过代码来控制动画的启动、暂停和停止等操作。因此,在实际开发中,可能需要根据具体的业务需求,来动态调整动画的播放状态。

四、Textview

1、文本缩写

(1)在 XML 布局文件中设置

<TextView
        android:id="@+id/my_text_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:maxLines="1"  // 设置 TextView 显示的最大行数为 1 行
        android:ellipsize="middle"  // 设置省略位置为中间
        android:text="这是一个很长的文本,需要省略显示" />

(2)在 Java 代码中设置

TextView textView = findViewById(R.id.my_text_view);
textView.setSingleLine(true);
textView.setMaxLines(1);
textView.setEllipsize(TextUtils.TruncateAt.MIDDLE);
textView.setText("这是一个很长的文本,需要省略显示");

五、PopupWindow

1、限制屏幕内显示

PopupWindow 在显示时可能会超出屏幕范围,为了保证 PopupWindow 能够完全显示在屏幕内,可以在 PopupWindow 显示前先计算 PopupWindow 的位置和大小,然后根据屏幕大小和 PopupWindow 的位置和大小,调整 PopupWindow 的位置。 下面是一个判断 PopupWindow 位置是否超出屏幕并调整位置的示例代码:

public void showPopupWindow(View anchorView, View contentView) {
    PopupWindow popupWindow = new PopupWindow(contentView,
            ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);

    // 计算 PopupWindow 在屏幕中的位置
    int[] location = new int[2];
    anchorView.getLocationOnScreen(location);
    int anchorX = location[0];
    int anchorY = location[1];
    int anchorWidth = anchorView.getWidth();
    int anchorHeight = anchorView.getHeight();

    int screenWidth = getResources().getDisplayMetrics().widthPixels;
    int screenHeight = getResources().getDisplayMetrics().heightPixels;

    // 判断 PopupWindow 是否超出屏幕范围
    boolean isOverX = anchorX + popupWindow.getWidth() > screenWidth;
    boolean isOverY = anchorY + anchorHeight + popupWindow.getHeight() > screenHeight;

    // 如果超出了屏幕范围,调整 PopupWindow 的位置
    if (isOverX) {
        popupWindow.setWidth(screenWidth - anchorX);
    }
    if (isOverY) {
        popupWindow.setHeight(screenHeight - anchorY - anchorHeight);
    }

    // 显示 PopupWindow
    popupWindow.showAtLocation(anchorView, Gravity.NO_GRAVITY, anchorX, anchorY + anchorHeight);
}

在这个示例代码中,我们首先创建了一个 PopupWindow,并计算了 PopupWindow 在屏幕中的位置。然后,我们获取了屏幕的大小,判断 PopupWindow 是否超出了屏幕范围,并根据情况调整 PopupWindow 的大小和位置。最后,我们调用了 PopupWindow 的 showAtLocation 方法,将 PopupWindow 显示在指定位置。

注意,在计算 PopupWindow 的位置和大小时,可能需要考虑状态栏和导航栏的高度。如果需要考虑状态栏和导航栏的高度,可以使用 ViewCompat.getWindowInsetsController 方法获取 WindowInsetsController,并从中获取状态栏和导航栏的高度。

六、ConstraintLayout

1、布局水平方向均分

要让子 View 在 ConstraintLayout 中水平方向上宽度均分为 50%,可以使用以下方法:

  1. 在 ConstraintLayout 中添加两个子 View,并且把它们分别放在布局的左侧和右侧,例如:
 <androidx.constraintlayout.widget.ConstraintLayout
    ...>

    <View
        android:id="@+id/view_left"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toStartOf="@+id/view_right"
        ... />

    <View
        android:id="@+id/view_right"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        app:layout_constraintStart_toEndOf="@+id/view_left"
        app:layout_constraintEnd_toEndOf="parent"
        ... />

</androidx.constraintlayout.widget.ConstraintLayout>

2、 然后在两个子 View 上添加水平方向上的权重属性,使它们在水平方向上均分布局的宽度。例如:

<androidx.constraintlayout.widget.ConstraintLayout
    ...>

    <View
        android:id="@+id/view_left"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toStartOf="@+id/view_right"
        app:layout_constraintHorizontal_weight="1"
        ... />

    <View
        android:id="@+id/view_right"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        app:layout_constraintStart_toEndOf="@+id/view_left"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_weight="1"
        ... />

</androidx.constraintlayout.widget.ConstraintLayout>

这样,两个子 View 在水平方向上就均分了布局的宽度。如果您想要更改子 View 的宽度比例,只需要调整它们的权重即可。例如,如果您想让左侧的子 View 宽度占 30%,右侧的子 View 宽度占 70%,可以将左侧的子 View 的权重设置为 3,右侧的子 View 的权重设置为 7。

七、动画

1、共享元素

(1)修改起始位置和大小

如果您需要使用RectF对象来获取共享元素的位置和大小信息,可以使用以下方法

RectF sharedElementRectF = new RectF();
int[] location = new int[2];
sharedElement.getLocationOnScreen(location);
sharedElementRectF.set(location[0], location[1], location[0] + sharedElement.getWidth(), location[1] + sharedElement.getHeight());

在上面的代码中,我们先通过View.getLocationOnScreen(int[])方法获取共享元素在屏幕上的坐标,然后使用这些坐标创建一个RectF对象表示共享元素在屏幕上的位置和大小信息。 然后,您可以将此RectF对象传递给ChangeBounds.setEpicenter()方法作为共享元素过渡的起始位置和大小。

以下是一个示例代码,展示了如何在setEnterSharedElementCallback()方法中使用RectF对象执行共享元素过渡动画:

setEnterSharedElementCallback(new SharedElementCallback() {
    @Override
    public void onSharedElementStart(List<String> sharedElementNames, List<View> sharedElements, List<View> sharedElementSnapshots) {
        super.onSharedElementStart(sharedElementNames, sharedElements, sharedElementSnapshots);

        // 获取共享元素的位置和大小信息
        RectF sharedElementRectF = new RectF();
        int[] location = new int[2];
        sharedElements.get(0).getLocationOnScreen(location);
        sharedElementRectF.set(location[0], location[1], location[0] + sharedElements.get(0).getWidth(), location[1] + sharedElements.get(0).getHeight());

        // 创建一个共享元素过渡对象,并设置起始位置和大小
        TransitionSet transitionSet = new TransitionSet();
        ChangeBounds changeBounds = new ChangeBounds();
        changeBounds.setEpicenter(sharedElementRectF);
        transitionSet.addTransition(changeBounds);

        // 启动共享元素过渡动画
        TransitionManager.beginDelayedTransition(container, transitionSet);
    }
});

在上面的示例中,我们使用RectF对象来创建共享元素过渡的起始位置和大小,将其传递给ChangeBounds.setEpicenter()方法,然后启动共享元素过渡动画。

  • 19
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值