Android之生成国庆节头像

2,250 阅读5分钟

效果

思路

刚开始是想用一张国旗图片蒙在图片上,然后把国旗图片的透明度调一下。但是实现出来效果不太好。

上面是国旗图片铺满和三分之二的效果。铺满头像就不够清晰了,三分之二的话没有渐变就太突兀了。

图片透明度调整是用的

binding.iv.setAlpha(100);//值范围设置为0-255

setAlpha已启用,官方给出使用setImageAlpha(int)代替

binding.iv.setImageAlpha(100);//值范围设置为0-255

所以想到的解决办法就是背景色和五角星分开,背景色从左到右渐变,五个五角星单独抠出来放到图片左上角实现。如果您有更好的解决方法欢迎留言。

实现

首先用到的库有glide和图片选择库

//glide
implementation 'com.github.bumptech.glide:glide:4.12.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0'
//图片选择框架
implementation 'io.github.lucksiege:pictureselector:v2.7.3-rc08'

完整布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical">
 
    <RelativeLayout
        android:id="@+id/rl"
        android:layout_width="300dp"
        android:layout_height="300dp">
 
        <ImageView
            android:id="@+id/iv"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_centerInParent="true"
            android:scaleType="centerCrop"
            android:src="@mipmap/ic_launcher" />
 
        <ImageView
            android:layout_width="200dp"
            android:layout_height="match_parent"
            android:background="@drawable/shape_bg" />
 
        <ImageView
            android:layout_width="150dp"
            android:layout_height="150dp"
            android:src="@drawable/wujiaoxing" />
    </RelativeLayout>
 
    <Button
        android:id="@+id/btn_choose"
        android:layout_width="300dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:background="@drawable/shape_bg"
        android:text="更换头像"
        android:textColor="@color/colorWhite" />
 
    <Button
        android:id="@+id/btn_save"
        android:layout_width="300dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:background="@drawable/shape_bg"
        android:text="保存头像"
        android:textColor="@color/colorWhite" />
</LinearLayout>

布局里最重要的是国旗渐变背景的实现,这里用了简单的资源背景方法实现渐变,shape_bg.xml文件如下:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
 
    <gradient
        android:angle="0"
        android:startColor="@color/colorRed" />
</shape>

我的颜色值文件

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="colorPrimary">#6200EE</color>
    <color name="colorPrimaryDark">#3700B3</color>
    <color name="colorAccent">#03DAC5</color>
 
    <color name="colorBlack">#000000</color>
    <color name="colorWhite">#ffffff</color>
    <color name="colorLine">#B3ffff</color>
    <color name="colorTransparency">#00000000</color>
    <color name="colorRed">#EE1C25</color>
</resources>

 图片资源请到项目中下载哦

主要代码实现

这里需要加入写权限和拍照权限

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /><!--写权限-->
<uses-permission android:name="android.permission.CAMERA" /> <!-- 拍照权限 -->

动态开启权限和保存头像功能的实现

binding.btnSave.setOnClickListener(v -> {
            if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
                ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 201);
            } else {
                getDraw();
            }
        });

 private void getDraw() {
        // 获取图片某布局
        binding.rl.setDrawingCacheEnabled(true);
        binding.rl.buildDrawingCache();
 
        mHandler.postDelayed(() -> {
            // 要在运行在子线程中
            final Bitmap bmp = binding.rl.getDrawingCache(); // 获取图片
            savePicture(bmp);// 保存图片
            binding.rl.destroyDrawingCache(); // 保存过后释放资源
        }, 100);
    }

@Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        switch (requestCode) {
            case 201:
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    getDraw();
                } else {
                    Toast.makeText(this, "您拒绝了权限的申请,可能无法进行下面的操作哦~", Toast.LENGTH_LONG).show();
                }
                break;
            case 202:
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    doCode();
                } else {
                    Toast.makeText(this, "您拒绝了权限的申请,可能无法进行下面的操作哦~", Toast.LENGTH_LONG).show();
                }
                break;
            default:
        }
    }

public void savePicture(Bitmap bm) {
        File file = createImageFile();
        //重新写入文件
        try {
            // 写入文件
            FileOutputStream fos;
            fos = new FileOutputStream(file);
            //默认jpg
            bm.compress(Bitmap.CompressFormat.PNG, 100, fos);
            fos.flush();
            fos.close();
            bm.recycle();
            sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(file)));
            Toast.makeText(this, "保存成功,请到相册中查看!", Toast.LENGTH_LONG).show();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
 
    public static File createImageFile() {
        File dir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "Pic");
        if (!dir.exists()) {
            boolean isSuccess = dir.mkdirs();
            Log.i("Pic", "文件夹创建状态--->" + isSuccess);
        }
        return new File(dir.getPath() + File.separator + "img_" + System.currentTimeMillis() + ".png");
    }

更换头像的实现

//更换头像
        binding.btnChoose.setOnClickListener(v -> {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
                    //没有权限则申请权限
                    ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 202);
                } else {
                    //有权限直接执行,docode()不用做处理
                    doCode();
                }
            } else {
                //小于6.0,不用申请权限,直接执行
                doCode();
            }
        });

private void doCode() {
        PictureSelectorUtils.ofImage(MainActivity.this, REQUEST_CODE_SELECT_USER_ICON);
    }
 
    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == REQUEST_CODE_SELECT_USER_ICON && resultCode == Activity.RESULT_OK) {
            String userIconPath = PictureSelectorUtils.forResult(resultCode, data);
            if (userIconPath != null) {
                Glide.with(this).load(userIconPath).into(binding.iv);
            }
        }
    }

图片选择用到的相关工具类

public class PictureSelectorUtils {
 
    public static void ofImage(Activity activity, int requestCode) {
        PictureSelector.create(activity)
                .openGallery(PictureMimeType.ofImage())
                .imageEngine(GlideEngine.createGlideEngine())
                .theme(R.style.PictureSelectorStyle)
                .selectionMode(PictureConfig.SINGLE)
                .enableCrop(false)//是否裁剪
                .isDragFrame(false)// 是否可拖动裁剪框(固定)
                .withAspectRatio(1, 1)// int 裁剪比例 如16:9 3:2 3:4 1:1 可自定义
                .isCamera(true)//是否显示拍照按钮 true or false
                .isGif(true)//是否显示gif图片 true or false
                .previewImage(true)// 是否可预览图片 true or false
                .forResult(requestCode);
    }
 
    public static String forResult(int resultCode, Intent data) {
        if (resultCode == Activity.RESULT_OK) {
            // 图片、视频、音频选择结果回调
            List<LocalMedia> selectList = PictureSelector.obtainMultipleResult(data);
            if (selectList != null && selectList.size() > 0) {
                return selectList.get(0).getPath();
            }
        }
        return null;
    }
}

public class GlideEngine implements ImageEngine {
 
    /**
     * 加载图片
     *
     * @param context
     * @param url
     * @param imageView
     */
    @Override
    public void loadImage(@NonNull Context context, @NonNull String url, @NonNull ImageView imageView) {
        if (!ImageLoaderUtils.assertValidRequest(context)) {
            return;
        }
        Glide.with(context)
                .load(url)
                .into(imageView);
    }
 
    /**
     * 加载网络图片适配长图方案
     * # 注意:此方法只有加载网络图片才会回调
     *
     * @param context
     * @param url
     * @param imageView
     * @param longImageView
     * @param callback      网络图片加载回调监听 {link after version 2.5.1 Please use the #OnImageCompleteCallback#}
     */
    @Override
    public void loadImage(@NonNull Context context, @NonNull String url,
                          @NonNull ImageView imageView,
                          SubsamplingScaleImageView longImageView, OnImageCompleteCallback callback) {
        if (!ImageLoaderUtils.assertValidRequest(context)) {
            return;
        }
        Glide.with(context)
                .asBitmap()
                .load(url)
                .into(new ImageViewTarget<Bitmap>(imageView) {
                    @Override
                    public void onLoadStarted(@Nullable Drawable placeholder) {
                        super.onLoadStarted(placeholder);
                        if (callback != null) {
                            callback.onShowLoading();
                        }
                    }
 
                    @Override
                    public void onLoadFailed(@Nullable Drawable errorDrawable) {
                        super.onLoadFailed(errorDrawable);
                        if (callback != null) {
                            callback.onHideLoading();
                        }
                    }
 
                    @Override
                    protected void setResource(@Nullable Bitmap resource) {
                        if (callback != null) {
                            callback.onHideLoading();
                        }
                        if (resource != null) {
                            boolean eqLongImage = MediaUtils.isLongImg(resource.getWidth(),
                                    resource.getHeight());
                            longImageView.setVisibility(eqLongImage ? View.VISIBLE : View.GONE);
                            imageView.setVisibility(eqLongImage ? View.GONE : View.VISIBLE);
                            if (eqLongImage) {
                                // 加载长图
                                longImageView.setQuickScaleEnabled(true);
                                longImageView.setZoomEnabled(true);
                                longImageView.setDoubleTapZoomDuration(100);
                                longImageView.setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_CENTER_CROP);
                                longImageView.setDoubleTapZoomDpi(SubsamplingScaleImageView.ZOOM_FOCUS_CENTER);
                                longImageView.setImage(ImageSource.cachedBitmap(resource),
                                        new ImageViewState(0, new PointF(0, 0), 0));
                            } else {
                                // 普通图片
                                imageView.setImageBitmap(resource);
                            }
                        }
                    }
                });
    }
 
    /**
     * 加载网络图片适配长图方案
     * # 注意:此方法只有加载网络图片才会回调
     *
     * @param context
     * @param url
     * @param imageView
     * @param longImageView
     * @ 已废弃
     */
    @Override
    public void loadImage(@NonNull Context context, @NonNull String url,
                          @NonNull ImageView imageView,
                          SubsamplingScaleImageView longImageView) {
        if (!ImageLoaderUtils.assertValidRequest(context)) {
            return;
        }
        Glide.with(context)
                .asBitmap()
                .load(url)
                .into(new ImageViewTarget<Bitmap>(imageView) {
                    @Override
                    protected void setResource(@Nullable Bitmap resource) {
                        if (resource != null) {
                            boolean eqLongImage = MediaUtils.isLongImg(resource.getWidth(),
                                    resource.getHeight());
                            longImageView.setVisibility(eqLongImage ? View.VISIBLE : View.GONE);
                            imageView.setVisibility(eqLongImage ? View.GONE : View.VISIBLE);
                            if (eqLongImage) {
                                // 加载长图
                                longImageView.setQuickScaleEnabled(true);
                                longImageView.setZoomEnabled(true);
                                longImageView.setDoubleTapZoomDuration(100);
                                longImageView.setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_CENTER_CROP);
                                longImageView.setDoubleTapZoomDpi(SubsamplingScaleImageView.ZOOM_FOCUS_CENTER);
                                longImageView.setImage(ImageSource.cachedBitmap(resource),
                                        new ImageViewState(0, new PointF(0, 0), 0));
                            } else {
                                // 普通图片
                                imageView.setImageBitmap(resource);
                            }
                        }
                    }
                });
    }
 
    /**
     * 加载相册目录
     *
     * @param context   上下文
     * @param url       图片路径
     * @param imageView 承载图片ImageView
     */
    @Override
    public void loadFolderImage(@NonNull Context context, @NonNull String url, @NonNull ImageView imageView) {
        if (!ImageLoaderUtils.assertValidRequest(context)) {
            return;
        }
        Glide.with(context)
                .asBitmap()
                .load(url)
                .override(180, 180)
                .centerCrop()
                .sizeMultiplier(0.5f)
                .placeholder(R.drawable.picture_image_placeholder)
                .into(new BitmapImageViewTarget(imageView) {
                    @Override
                    protected void setResource(Bitmap resource) {
                        RoundedBitmapDrawable circularBitmapDrawable =
                                RoundedBitmapDrawableFactory.
                                        create(context.getResources(), resource);
                        circularBitmapDrawable.setCornerRadius(8);
                        imageView.setImageDrawable(circularBitmapDrawable);
                    }
                });
    }
 
 
    /**
     * 加载gif
     *
     * @param context   上下文
     * @param url       图片路径
     * @param imageView 承载图片ImageView
     */
    @Override
    public void loadAsGifImage(@NonNull Context context, @NonNull String url,
                               @NonNull ImageView imageView) {
        if (!ImageLoaderUtils.assertValidRequest(context)) {
            return;
        }
        Glide.with(context)
                .asGif()
                .load(url)
                .into(imageView);
    }
 
    /**
     * 加载图片列表图片
     *
     * @param context   上下文
     * @param url       图片路径
     * @param imageView 承载图片ImageView
     */
    @Override
    public void loadGridImage(@NonNull Context context, @NonNull String url, @NonNull ImageView imageView) {
        if (!ImageLoaderUtils.assertValidRequest(context)) {
            return;
        }
        Glide.with(context)
                .load(url)
                .override(200, 200)
                .centerCrop()
                .placeholder(R.drawable.picture_image_placeholder)
                .into(imageView);
    }
 
 
    private GlideEngine() {
    }
 
    private static GlideEngine instance;
 
    public static GlideEngine createGlideEngine() {
        if (null == instance) {
            synchronized (GlideEngine.class) {
                if (null == instance) {
                    instance = new GlideEngine();
                }
            }
        }
        return instance;
    }
}

public class ImageLoaderUtils {
    public static boolean assertValidRequest(Context context) {
        if (context instanceof Activity) {
            Activity activity = (Activity) context;
            return !isDestroy(activity);
        } else if (context instanceof ContextWrapper){
            ContextWrapper contextWrapper = (ContextWrapper) context;
            if (contextWrapper.getBaseContext() instanceof Activity){
                Activity activity = (Activity) contextWrapper.getBaseContext();
                return !isDestroy(activity);
            }
        }
        return true;
    }
 
    private static boolean isDestroy(Activity activity) {
        if (activity == null) {
            return true;
        }
        return activity.isFinishing() || activity.isDestroyed();
    }
}

图片选择页面自定义样式

<style name="PictureSelectorStyle" parent="picture.default.style">
        <!--标题栏背景色-->
        <item name="colorPrimary">@color/colorPrimaryDark</item>
        <!--状态栏背景色-->
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <!--是否改变图片列表界面状态栏字体颜色为黑色-->
        <!--<item name="picture.statusFontColor">false</item>-->
        <!--返回键图标-->
        <!--<item name="picture.leftBack.icon">@drawable/ic_back</item>-->
        <!--标题下拉箭头-->
        <!--<item name="picture.arrow_down.icon">@drawable/arrow_down</item>-->
        <!--标题上拉箭头-->
        <!--<item name="picture.arrow_up.icon">@drawable/arrow_up</item>-->
        <!--标题文字颜色-->
        <!--<item name="picture.title.textColor">@color/basic_ui_action_bar_text</item>-->
        <!--标题栏右边文字-->
        <!--<item name="picture.right.textColor">@color/basic_ui_action_bar_text</item>-->
        <!--图片列表勾选样式-->
        <!--<item name="picture.checked.style">@drawable/picture_selector_checkbox</item>-->
        <!--开启图片列表勾选数字模式-->
        <item name="picture.style.checkNumMode">false</item>
        <!--选择图片样式0/9-->
        <item name="picture.style.numComplete">false</item>
        <!--图片列表底部背景色-->
        <item name="picture.bottom.bg">@color/colorPrimaryDark</item>
        <!--图片列表预览文字颜色-->
        <!--<item name="picture.preview.textColor">@color/picture_selector_preview_text_color</item>-->
        <!--图片列表已完成文字颜色-->
        <!-- <item name="picture.complete.textColor">@color/picture_selector_preview_text_color</item>-->
        <!--图片已选数量圆点背景色-->
        <!--<item name="picture.num.style">@drawable/picture_selector_num_oval</item>-->
        <!--预览界面标题文字颜色-->
        <!--<item name="picture.ac_preview.title.textColor">@color/basic_ui_action_bar_text</item>-->
        <!--预览界面已完成文字颜色-->
        <!--<item name="picture.ac_preview.complete.textColor">@color/picture_selector_preview_text_color</item>-->
        <!--预览界面标题栏背景色-->
        <item name="picture.ac_preview.title.bg">@color/colorPrimaryDark</item>
        <!--预览界面底部背景色-->
        <item name="picture.ac_preview.bottom.bg">@color/colorPrimaryDark</item>
        <!--预览界面返回箭头-->
        <!--<item name="picture.preview.leftBack.icon">@drawable/ic_back</item>-->
        <!--裁剪页面标题背景色-->
        <!--<item name="picture.crop.toolbar.bg">@color/basic_ui_action_bar_bg</item>-->
        <!--裁剪页面状态栏颜色-->
        <!--<item name="picture.crop.status.color">@color/basic_ui_action_bar_bg</item>-->
        <!--裁剪页面标题文字颜色-->
        <!--<item name="picture.crop.title.color">@color/basic_ui_action_bar_text</item>-->
        <!--相册文件夹列表选中图标-->
        <!--<item name="picture.folder_checked_dot">@drawable/orange_oval</item>-->
    </style>

对了Android10以上多了分区存储限制,所以10和10以上的要开启外部存储才能把图片保存到相册下。

android:requestLegacyExternalStorage="true"

官方给的解决方法是: 为了提高用户隐私,不推荐直接访问共享/外部 * 存储设备。当应用以 * {@link android.os.Build.VERSION_CODES#Q} 为目标时,此方法返回的 * 路径不再可供应用直接访问。 * 应用程序可以通过迁移到诸如 * {@link Context#getExternalFilesDir(String)}、* {@link MediaStore} 或 {@link Intent#ACTION_OPEN_DOCUMENT} 等替代方案来继续访问存储在共享/外部 * 存储上的内容。 

这样简单的国庆头像就简单完成了,除了选择图片和保存图片代码稍微多一点,其他重要的也就是布局中的背景渐变了。

详细代码地址请参考: github.com/cuiwenju201…