效果
思路
刚开始是想用一张国旗图片蒙在图片上,然后把国旗图片的透明度调一下。但是实现出来效果不太好。
上面是国旗图片铺满和三分之二的效果。铺满头像就不够清晰了,三分之二的话没有渐变就太突兀了。
图片透明度调整是用的
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…