前语

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()办法,然后启动同享元素过渡动画。