The effect

Train of thought

At first, I wanted to use a flag picture over the picture, and then adjust the transparency of the flag picture. But it didn’t work out very well.

Above is the flag picture spread full and two-thirds effect. It’s not clear enough to cover your head, and it’s too obtrusive to cover two-thirds of your head with no gradient.

Image opacity adjustment is used

binding.iv.setAlpha(100); // The value ranges from 0 to 255Copy the code

– setAlpha is enabled, use setImageAlpha(int) instead

binding.iv.setImageAlpha(100); // The value ranges from 0 to 255Copy the code

So the solution I came up with was to separate the background color from the pentacle, gradient the background color from left to right, and cut out the five pentacles and put them in the upper left corner of the image. If you have a better solution, please leave a message.

implementation

The first libraries used were Glide and the Picture selection library

/ / glide implementation 'com. Making. Bumptech. Glide: glide: 4.12.0' annotationProcessor 'com. Making. Bumptech. Glide: the compiler: 4.12.0' / / picture selection framework implementation 'IO. Making. Lucksiege: pictureselector: v2.7.3 - rc08'Copy the code

Complete the layout

<? The 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=" 1 "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=" Save your profile" android:textColor="@color/colorWhite" /> </LinearLayout>Copy the code

The most important part of the layout is the implementation of the flag gradient background. Here we use the simple resource background method to achieve the gradient. The shape_bg.xml file is as follows:

<? The 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>Copy the code

My color values file

<? The 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>Copy the code

Picture resources please download in the project oh

Main code implementation

You need to add write permission and photo permission

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /><! - write permissions - > < USES - permission android: name = "android. Permission. CAMERA" / > <! -- Photo permission -->Copy the code

Dynamically open permissions and save the realization of the avatar function

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 () {/ / get photo a layout binding. Rl. SetDrawingCacheEnabled (true); binding.rl.buildDrawingCache(); MHandler. PostDelayed (() - > {/ / want to be in operation in the child thread final Bitmap BMP = binding. Rl. GetDrawingCache (); SavePicture (BMP); / / save picture binding. The 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 ", toast.length_long).show(); } break; case 202: if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { doCode(); } else {toast.maketext (this, "toast.length_long ", toast.length_long).show(); } break; default: } } public void savePicture(Bitmap bm) { File file = createImageFile(); // Write FileOutputStream fos; // write FileOutputStream fos; fos = new FileOutputStream(file); / / the default JPG bm.com press (Bitmap.Com pressFormat. PNG, 100, fos); fos.flush(); fos.close(); bm.recycle(); sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(file))); Toast.makeText(this, "Save successfully, please view in the album!" , 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", "folder creation status -->" + isSuccess); } return new File(dir.getPath() + File.separator + "img_" + System.currentTimeMillis() + ".png"); }Copy the code

Change the realization of the avatar

/ / replace head binding. BtnChoose. SetOnClickListener (v - > {the if (Build) VERSION) SDK_INT > = Build. VERSION_CODES. M) {if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) ! = PackageManager. PERMISSION_GRANTED) {/ / permissions are not applied for permission to ActivityCompat requestPermissions (MainActivity. This, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 202); } else {// Have permission to execute docode() without doing docode(); } else {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); }}}Copy the code

Image selection to use the relevant tool class

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();
    }
}
Copy the code

Image selection page custom style

<style name="PictureSelectorStyle" parent="picture.default.style"> <! <item name="colorPrimary">@color/colorPrimaryDark</item> <! <item name="colorPrimaryDark">@color/colorPrimaryDark</item> <! Change the status bar font color to black --> <! --<item name="picture.statusFontColor">false</item>--> <! -- Return key icon --> <! --<item name="picture.leftBack.icon">@drawable/ic_back</item>--> <! -- Title drop down arrow --> <! --<item name="picture.arrow_down.icon">@drawable/arrow_down</item>--> <! -- Title pull arrow --> <! --<item name="picture.arrow_up.icon">@drawable/arrow_up</item>--> <! -- Title text color --> <! --<item name="picture.title.textColor">@color/basic_ui_action_bar_text</item>--> <! -- Text to the right of the title bar --> <! --<item name="picture.right.textColor">@color/basic_ui_action_bar_text</item>--> <! -- Picture list check style --> <! --<item name="picture.checked.style">@drawable/picture_selector_checkbox</item>--> <! - open image list check the digital mode - > < item name = "picture. Style. CheckNumMode" > false < / item > <! - choose style picture 0/9 - > < item name = "picture. Style. NumComplete" > false < / item > <! <item name="picture.bottom.bg">@color/colorPrimaryDark</item> <! Image list preview text color --> <! --<item name="picture.preview.textColor">@color/picture_selector_preview_text_color</item>--> <! Image list completed text color --> <! -- <item name="picture.complete.textColor">@color/picture_selector_preview_text_color</item>--> <! Image selected number of dots background color --> <! --<item name="picture.num.style">@drawable/picture_selector_num_oval</item>--> <! Preview screen title text color --> <! --<item name="picture.ac_preview.title.textColor">@color/basic_ui_action_bar_text</item>--> <! Preview screen completed text color --> <! --<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> <! -- Preview screen returns arrow --> <! --<item name="picture.preview.leftBack.icon">@drawable/ic_back</item>--> <! Cropping page title background color --> <! --<item name="picture.crop.toolbar.bg">@color/basic_ui_action_bar_bg</item>--> <! Cropping page status bar color --> <! --<item name="picture.crop.status.color">@color/basic_ui_action_bar_bg</item>--> <! Cropping page title text color --> <! --<item name="picture.crop.title.color">@color/basic_ui_action_bar_text</item>--> <! -- Album folder list check icon --> <! --<item name="picture.folder_checked_dot">@drawable/orange_oval</item>--> </style>Copy the code

In Android10 and above, there are more partition storage restrictions, so 10 and above need to enable external storage to save photos to albums.

android:requestLegacyExternalStorage="true"
Copy the code

The official solution: To improve user privacy, direct access to shared/external * storage devices is not recommended. When an application targets * {@link android.os.build.version_codes #Q}, the * path returned by this method is no longer directly accessible to the application. * Applications can be migrated to sites such as * {@link Context#getExternalFilesDir(String)}, * {@link MediaStore} or {@link Intent#ACTION_OPEN_DOCUMENT}. To continue accessing content stored on shared/external * storage.

This simple National Day avatar is done, except for selecting the image and saving the image with a little more code, the other important thing is the background gradient in the layout.

Please refer to github.com/cuiwenju201…