effect
thinking
At first, I wanted to cover the picture with a picture of the national flag, and then adjust the transparency of the picture of the national flag. But the effect is not very good.
Above is a picture of the national flag covered with and two-thirds of the effect. It's not clear enough to cover the head. It's too abrupt if there is no gradient for two-thirds.
Picture transparency adjustment is used
binding.iv.setAlpha(100);//The value range is set to 0-255
setAlpha has been enabled. It is officially given to use setImageAlpha(int) instead
binding.iv.setImageAlpha(100);//The value range is set to 0-255
So the solution is to separate the background color from the five pointed stars. The background color gradually changes from left to right. The five five pointed stars are pulled out separately and placed in the upper left corner of the picture. If you have a better solution, please leave a message.
realization
The first libraries used are glide and picture selection library
//glide implementation 'com.github.bumptech.glide:glide:4.12.0' annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0' //Picture selection frame implementation 'io.github.lucksiege:pictureselector:v2.7.3-rc08'
Complete layout
<?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="change the avatar" 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 Avatar" android:textColor="@color/colorWhite" /> </LinearLayout>
The most important thing in the layout is the realization of flag gradient background. Here, a simple resource background method is used to realize gradient, shape_bg.xml file is as follows:
<?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>
My color value file
<?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>
Please download the picture resources in the project
Main code implementation
Write permission and photo permission need to be added here
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /><!--Write permission--> <uses-permission android:name="android.permission.CAMERA" /> <!-- Photographing authority -->
Implementation of dynamic opening permission and saving 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 a picture layout binding.rl.setDrawingCacheEnabled(true); binding.rl.buildDrawingCache(); mHandler.postDelayed(() -> { // To run in a child thread final Bitmap bmp = binding.rl.getDrawingCache(); // Get picture savePicture(bmp);// Save picture binding.rl.destroyDrawingCache(); // Release resources after saving }, 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, "You have rejected the permission application. You may not be able to perform the following operations~", Toast.LENGTH_LONG).show(); } break; case 202: if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { doCode(); } else { Toast.makeText(this, "You have rejected the permission application. You may not be able to perform the following operations~", Toast.LENGTH_LONG).show(); } break; default: } }
public void savePicture(Bitmap bm) { File file = createImageFile(); //Rewrite file try { // write file FileOutputStream fos; fos = new FileOutputStream(file); //Default 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, "Saved successfully,Please check 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"); }
Implementation of changing Avatar
//change the avatar 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) { //If you don't have permission, apply for permission ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 202); } else { //You have permission to execute directly without processing docode() doCode(); } } else { //If it is less than 6.0, it can be executed directly without applying for permission 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); } } }
Related tools used for image selection
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)//Crop .isDragFrame(false)// Can I drag the crop box (fixed) .withAspectRatio(1, 1)// int clipping scale, such as 16:9 3:2 3:4 1:1, can be customized .isCamera(true)//Whether to display the Photo button true or false .isGif(true)//Whether to display gif picture true or false .previewImage(true)// Can I preview the picture? true or false .forResult(requestCode); } public static String forResult(int resultCode, Intent data) { if (resultCode == Activity.RESULT_OK) { // Callback of picture, video and audio selection results List<LocalMedia> selectList = PictureSelector.obtainMultipleResult(data); if (selectList != null && selectList.size() > 0) { return selectList.get(0).getPath(); } } return null; } }
public class GlideEngine implements ImageEngine { /** * Load picture * * @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); } /** * Load network picture adaptation long picture scheme * # Note: this method only calls back when loading network pictures * * @param context * @param url * @param imageView * @param longImageView * @param callback Network picture loading callback listening {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) { // Load long graph 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 { // Ordinary picture imageView.setImageBitmap(resource); } } } }); } /** * Load network picture adaptation long picture scheme * # Note: this method only calls back when loading network pictures * * @param context * @param url * @param imageView * @param longImageView * @ obsolete */ @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) { // Load long graph 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 { // Ordinary picture imageView.setImageBitmap(resource); } } } }); } /** * Load album directory * * @param context context * @param url Picture path * @param imageView Host image 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); } }); } /** * Load gif * * @param context context * @param url Picture path * @param imageView Host image 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); } /** * Load picture list picture * * @param context context * @param url Picture path * @param imageView Host image 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(); } }
Picture selection page custom style
<style name="PictureSelectorStyle" parent="picture.default.style"> <!--Title block background color--> <item name="colorPrimary">@color/colorPrimaryDark</item> <!--Status bar background color--> <item name="colorPrimaryDark">@color/colorPrimaryDark</item> <!--Do you want to change the font color of the status bar of the picture list interface 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 up 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>--> <!--Title Block right text--> <!--<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>--> <!--Turn on the picture list and check the digital mode--> <item name="picture.style.checkNumMode">false</item> <!--Select Picture Style 0/9--> <item name="picture.style.numComplete">false</item> <!--Background color at the bottom of the picture list--> <item name="picture.bottom.bg">@color/colorPrimaryDark</item> <!--Picture list preview text color--> <!--<item name="picture.preview.textColor">@color/picture_selector_preview_text_color</item>--> <!--Picture list finished text color--> <!-- <item name="picture.complete.textColor">@color/picture_selector_preview_text_color</item>--> <!--Number of dots selected in picture background color--> <!--<item name="picture.num.style">@drawable/picture_selector_num_oval</item>--> <!--Preview interface title text color--> <!--<item name="picture.ac_preview.title.textColor">@color/basic_ui_action_bar_text</item>--> <!--Preview interface completed text color--> <!--<item name="picture.ac_preview.complete.textColor">@color/picture_selector_preview_text_color</item>--> <!--Preview interface title bar background color--> <item name="picture.ac_preview.title.bg">@color/colorPrimaryDark</item> <!--Background color at the bottom of preview interface--> <item name="picture.ac_preview.bottom.bg">@color/colorPrimaryDark</item> <!--Preview interface return arrow--> <!--<item name="picture.preview.leftBack.icon">@drawable/ic_back</item>--> <!--Crop page title background color--> <!--<item name="picture.crop.toolbar.bg">@color/basic_ui_action_bar_bg</item>--> <!--Crop page status bar color--> <!--<item name="picture.crop.status.color">@color/basic_ui_action_bar_bg</item>--> <!--Crop 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>
For Android 10 and above, there are more partition storage restrictions, so for 10 and above, you need to turn on external storage to save the pictures to the album.
android:requestLegacyExternalStorage="true"
The official solution is:
To improve user privacy, direct access to shared / external * storage devices is not recommended. When the application targets * {@ link android.os.Build.VERSION_CODES#Q}, the * path returned by this method is no longer available for direct access* Applications can continue to access content stored on shared / external * storage by migrating to alternatives such as * {@ link Context#getExternalFilesDir(String)}, * {@ link MediaStore}, or {@ link Intent#ACTION_OPEN_DOCUMENT}.
Such a simple National Day avatar is simply completed. In addition to selecting pictures and saving picture codes a little more, the other important thing is the background gradient in the layout.
For detailed code address, please refer to:
Create national day Avatarhttps://github.com/cuiwenju2017/CreateNationalDayHead