public class AlertController {
private AlertDialog mAlertDialog;
private Window mWindow;
private DialogViewHelper mViewHelper;
public AlertController(AlertDialog alertDialog, Window window) {
mAlertDialog = alertDialog;
mWindow = window;
}
public void setDialogViewHelper(DialogViewHelper dialogViewHelper) {
mViewHelper = dialogViewHelper;
}
public void setText(int viewId, CharSequence text) {
mViewHelper.setText(viewId, text);
}
public void setIcon(int viewId, int resId) {
mViewHelper.setIcon(viewId, resId);
}
public T getView(int viewId) {
return mViewHelper.getView(viewId);
}
public void setOnClickListener(int viewId, View.OnClickListener onClickListener) {
mViewHelper.setOnClickListener(viewId, onClickListener);
}
public AlertDialog getDialog() {
return mAlertDialog;
}
public Window getWindow() {
return mWindow;
}
//-------------------------------------------------------------------------------------------------
public static class AlertParams {
public Context mContext;
//对话框主题背景
public int mThemeResId;
public boolean mCancelable;
public DialogInterface.OnCancelListener mOnCancelListener;
public DialogInterface.OnDismissListener mOnDismissListener;
public DialogInterface.OnKeyListener mOnKeyListener;
//文本颜色
public SparseArray mTextColorArray = new SparseArray<>();
//存放文本的更改
public SparseArray mTextArray = new SparseArray<>();
//存放点击事件
public SparseArray<View.OnClickListener> mClickArray = new SparseArray<>();
//存放长按点击事件
public SparseArray<View.OnLongClickListener> mLondClickArray = new SparseArray<>();
//存放对话框图标
public SparseArray mIconArray = new SparseArray<>();
//存放对话框图片
public SparseArray mBitmapArray = new SparseArray<>();
//对话框布局资源id
public int mLayoutResId;
//对话框的view
public View mView;
//对话框宽度
public int mWidth;
//对话框高度
public int mHeight;
//对话框垂直外边距
public int mHeightMargin;
//对话框横向外边距
public int mWidthMargin;
//动画
public int mAnimation;
//对话框显示位置
public int mGravity = Gravity.CENTER;
public AlertParams(Context context, int themeResId) {
mContext = context;
mThemeResId = themeResId;
}
public void apply(AlertController alert) {
//设置对话框布局
DialogViewHelper dialogViewHelper = null;
if (mLayoutResId != 0) {
dialogViewHelper = new DialogViewHelper(mContext, mLayoutResId);
}
if (mView != null) {
dialogViewHelper = new DialogViewHelper();
dialogViewHelper.setContentView(mView);
}
if (dialogViewHelper == null) {
throw new IllegalArgumentException(“please set layout”);
}
//将对话框布局设置到对话框
alert.getDialog().setContentView(dialogViewHelper.getContentView());
//设置DialogViewHelper辅助类
alert.setDialogViewHelper(dialogViewHelper);
//设置文本
for (int i = 0; i < mTextArray.size(); i++) {
alert.setText(mTextArray.keyAt(i), mTextArray.valueAt(i));
}
//设置图标
for (int i = 0; i < mIconArray.size(); i++) {
alert.setIcon(mIconArray.keyAt(i), mIconArray.valueAt(i));
}
//设置点击
for (int i = 0; i < mClickArray.size(); i++) {
alert.setOnClickListener(mClickArray.keyAt(i), mClickArray.valueAt(i));
}
//配置自定义效果,底部弹出,宽高,动画,全屏
Window window = alert.getWindow();
window.setGravity(mGravity);//显示位置
if (mAnimation != 0) {
window.setWindowAnimations(mAnimation);//设置动画
}
//设置宽高
WindowManager.LayoutParams params = window.getAttributes();
params.width = mWidth;
params.height = mHeight;
params.verticalMargin = mHeightMargin;
params.horizontalMargin = mWidthMargin;
window.setAttributes(params);
}
}
}
下面自定义Dialog
③ AlertDialog
在dialog包下新建一个AlertDialog,里面的代码如下:
public class AlertDialog extends Dialog {
private AlertController mAlert;
public AlertDialog(@NonNull Context context, @StyleRes int themeResId) {
super(context, themeResId);
mAlert = new AlertController(this, getWindow());
}
public void setText(int viewId, CharSequence text) {
mAlert.setText(viewId, text);
}
public T getView(int viewId) {
return mAlert.getView(viewId);
}
public void setOnClickListener(int viewId, View.OnClickListener onClickListener) {
mAlert.setOnClickListener(viewId, onClickListener);
}
//----------------------------------------------------------------------------------------------
public static class Builder {
private final AlertController.AlertParams P;
public Builder(Context context) {
this(context, R.style.dialog);
}
public Builder(Context context, int themeResId) {
P = new AlertController.AlertParams(context, themeResId);
}
/**
-
设置对话框布局
-
@param view
-
@return
*/
public Builder setContentView(View view) {
P.mView = view;
P.mLayoutResId = 0;
return this;
}
/**
-
@param layoutId
-
@return
*/
public Builder setContentView(int layoutId) {
P.mView = null;
P.mLayoutResId = layoutId;
return this;
}
/**
-
设置文本
-
@param viewId
-
@param text
-
@return
*/
public Builder setText(int viewId, CharSequence text) {
P.mTextArray.put(viewId, text);
return this;
}
/**
-
设置文本颜色
-
@param viewId
-
@param color
-
@return
*/
public Builder setTextColor(int viewId, int color) {
P.mTextColorArray.put(viewId, color);
return this;
}
/**
-
设置图标
-
@param iconId
-
@return
*/
public Builder setIcon(int iconId, int resId) {
P.mIconArray.put(iconId, resId);
return this;
}
/**
-
设置图片
-
@param viewId
-
@return
*/
public Builder setBitmap(int viewId, Bitmap bitmap) {
P.mBitmapArray.put(viewId, bitmap);
return this;
}
/**
-
设置对话框宽度占满屏幕
-
@return
*/
public Builder fullWidth() {
P.mWidth = ViewGroup.LayoutParams.MATCH_PARENT;
return this;
}
/**
-
对话框底部弹出
-
@param isAnimation
-
@return
*/
public Builder fromBottom(boolean isAnimation) {
if (isAnimation) {
P.mAnimation = R.style.dialog_from_bottom_anim;
}
P.mGravity = Gravity.BOTTOM;
return this;
}
/**
-
对话框右部弹出
-
@param isAnimation
-
@return
*/
public Builder fromRight(boolean isAnimation) {
if (isAnimation) {
P.mAnimation = R.style.dialog_scale_anim;
}
P.mGravity = Gravity.RIGHT;
return this;
}
/**
-
设置对话框宽高
-
@param width
-
@param heigth
-
@return
*/
public Builder setWidthAndHeight(int width, int heigth) {
P.mWidth = width;
P.mHeight = heigth;
return this;
}
/**
-
设置对话框宽高
-
@param width
-
@param heigth
-
@return
*/
public Builder setWidthAndHeightMargin(int width, int heigth, int heightMargin, int widthMargin) {
P.mWidth = width;
P.mHeight = heigth;
P.mHeightMargin = heightMargin;
P.mWidthMargin = widthMargin;
return this;
}
/**
-
添加默认动画
-
@return
*/
public Builder addDefaultAnimation() {
P.mAnimation = R.style.dialog_scale_anim;
return this;
}
/**
-
设置动画
-
@param styleAnimation
-
@return
*/
public Builder setAnimation(int styleAnimation) {
P.mAnimation = styleAnimation;
return this;
}
/**
-
设置点击事件
-
@param viewId
-
@param onClickListener
-
@return
*/
public Builder setOnClickListener(int viewId, View.OnClickListener onClickListener) {
P.mClickArray.put(viewId, onClickListener);
return this;
}
public Builder setOnLongClickListener(int viewId, View.OnLongClickListener onLongClickListener) {
P.mLondClickArray.put(viewId, onLongClickListener);
return this;
}
/**
-
Sets whether the dialog is cancelable or not. Default is true.
-
@return This Builder object to allow for chaining of calls to set methods
*/
public Builder setCancelable(boolean cancelable) {
P.mCancelable = cancelable;
return this;
}
public Builder setOnCancelListener(OnCancelListener onCancelListener) {
P.mOnCancelListener = onCancelListener;
return this;
}
public Builder setOnDismissListener(OnDismissListener onDismissListener) {
P.mOnDismissListener = onDismissListener;
return this;
}
public Builder setOnKeyListener(OnKeyListener onKeyListener) {
P.mOnKeyListener = onKeyListener;
return this;
}
public AlertDialog create() {
// Context has already been wrapped with the appropriate theme.
final AlertDialog dialog = new AlertDialog(P.mContext, P.mThemeResId);
P.apply(dialog.mAlert);
dialog.setCancelable(P.mCancelable);
if (P.mCancelable) {
dialog.setCanceledOnTouchOutside(true);
}
dialog.setOnCancelListener(P.mOnCancelListener);
dialog.setOnDismissListener(P.mOnDismissListener);
if (P.mOnKeyListener != null) {
dialog.setOnKeyListener(P.mOnKeyListener);
}
return dialog;
}
public AlertDialog show() {
final AlertDialog dialog = create();
dialog.show();
return dialog;
}
}
}
④ 样式
在设置弹窗的样式和弹窗出现的方式,在themes.xml下新增如下代码:
这里还用到动画样式文件,在res文件夹下新建一个anim文件夹,里面定义了7个xml文件,如下所示:
新建dialog_from_bottom_anim_in.xml,代码如下:
<?xml version="1.0" encoding="utf-8"?><translate
android:duration=“400”
android:fromXDelta=“0”
android:fromYDelta=“1000”
android:toXDelta=“0”
android:toYDelta=“0” />
dialog_from_bottom_anim_out.xml,代码如下:
<?xml version="1.0" encoding="utf-8"?><translate
android:duration=“400”
android:fromXDelta=“0”
android:fromYDelta=“0”
android:toXDelta=“0”
android:toYDelta=“1000” />
dialog_from_top_anim_in.xml,代码如下:
<?xml version="1.0" encoding="utf-8"?><translate
android:duration=“1000”
android:fromYDelta=“-100%”
android:toYDelta=“0” />
dialog_from_top_anim_out.xml,代码如下:
<?xml version="1.0" encoding="utf-8"?><translate
android:duration=“1000”
android:fromYDelta=“0”
android:toYDelta=“-100%” />
dialog_scale_anim_in.xml,代码如下:
<?xml version="1.0" encoding="utf-8"?><scale
android:duration=“135”
android:fromXScale=“0.8”
android:fromYScale=“0.8”
android:pivotX=“50%”
android:pivotY=“50%”
android:toXScale=“1.05”
android:toYScale=“1.05” />
<scale
android:duration=“105”
android:fromXScale=“1.05”
android:fromYScale=“1.05”
android:pivotX=“50%”
android:pivotY=“50%”
android:startOffset=“135”
android:toXScale=“0.95”
android:toYScale=“0.95” />
<scale
android:duration=“60”
android:fromXScale=“0.95”
android:fromYScale=“0.95”
android:pivotX=“50%”
android:pivotY=“50%”
android:startOffset=“240”
android:toXScale=“1.0”
android:toYScale=“1.0” />
<alpha
android:duration=“90”
android:fromAlpha=“0.0”
android:interpolator=“@android:anim/accelerate_interpolator”
android:toAlpha=“1.0” />
dialog_scale_anim_out.xml,代码如下:
<?xml version="1.0" encoding="utf-8"?><scale
android:duration=“150”
android:fromXScale=“1.0”
android:fromYScale=“1.0”
android:pivotX=“50%”
android:pivotY=“50%”
android:toXScale=“0.6”
android:toYScale=“0.6” />
<alpha
android:duration=“150”
android:fromAlpha=“1.0”
android:interpolator=“@android:anim/accelerate_interpolator”
android:toAlpha=“0.0” />
loading_animation.xml,代码如下:
<?xml version="1.0" encoding="utf-8"?><rotate
android:interpolator=“@android:anim/linear_interpolator”
android:pivotX=“50%”
android:pivotY=“50%”
android:fromDegrees=“0”
android:toDegrees=“+360”
android:duration=“1500”
android:startOffset=“-1”
android:repeatMode=“restart”
android:repeatCount=“-1”/>
这里还有一个shape_bg_white_radius_6.xml样式,在drawable中创建,里面的代码如下:
<?xml version="1.0" encoding="utf-8"?>同样再创建一个shape_bg_white_radius_12.xml,代码如下:
<?xml version="1.0" encoding="utf-8"?>还有一个shape_bg_white_radius_24.xml,代码如下:
<?xml version="1.0" encoding="utf-8"?>⑤ 布局
在本文章将会创建三个弹窗布局,一个用于表示加载状态,一个用于表示修改用户信息,最后一个用于输入信息。
在layout下新建一个dialog_edit.xml,代码如下:
<?xml version="1.0" encoding="utf-8"?><RelativeLayout
android:layout_width=“300dp”
android:layout_height=“wrap_content”
android:background=“@drawable/shape_bg_white_radius_12”>
<TextView
android:id=“@+id/tv_title”
android:layout_width=“match_parent”
android:layout_height=“wrap_content”
android:gravity=“center”
android:padding=“12dp”
android:text=“标题”
android:textSize=“16sp” />
<androidx.appcompat.widget.AppCompatEditText
android:id=“@+id/et_content”
android:layout_width=“match_parent”
android:layout_height=“wrap_content”
android:layout_below=“@+id/tv_title”
android:layout_margin=“12dp”
android:maxLength=“18”
android:singleLine=“true”
android:textSize=“@dimen/sp_14” />
<View
android:id=“@+id/v_line”
android:layout_width=“match_parent”
android:layout_height=“1dp”
android:layout_below=“@+id/et_content”
android:background=“@color/line” />
<TextView
android:id=“@+id/tv_cancel”
android:layout_width=“150dp”
android:layout_height=“50dp”
android:textSize=“@dimen/sp_14”
android:layout_below=“@+id/v_line”
android:foreground=“?attr/selectableItemBackground”
android:gravity=“center”
android:text=“取消” />
<View
android:layout_width=“1dp”
android:layout_height=“50dp”
android:layout_below=“@+id/v_line”
android:layout_centerHorizontal=“true”
android:background=“@color/line” />
<TextView
android:id=“@+id/tv_sure”
android:layout_width=“150dp”
android:layout_height=“50dp”
android:textColor=“@color/purple_500”
android:layout_below=“@+id/v_line”
android:textSize=“@dimen/sp_14”
android:layout_toEndOf=“@+id/tv_cancel”
android:foreground=“?attr/selectableItemBackground”
android:gravity=“center”
android:text=“确定” />
在layout下新建一个dialog_loading.xml,代码如下:
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android=“http://schemas.android.com/apk/res/android”
android:id=“@+id/dialog_view”
android:orientation=“vertical”
android:layout_width=“120dp”
android:layout_height=“120dp”
android:gravity=“center”
android:padding=“10dp”>
<ImageView
android:id=“@+id/iv_loading”
android:layout_width=“40dp”
android:layout_height=“40dp”
android:src=“@mipmap/ic_loading” />
<TextView
android:id=“@+id/tv_loading_tx”
android:layout_width=“wrap_content”
android:layout_height=“wrap_content”
android:layout_marginTop=“10dp”
android:maxLines=“1”
android:text=“Loading…”
android:textColor=“@color/purple_500”
android:textSize=“14sp” />
这里有一个图标
放在mipmap下。
最后在layout下新建一个dialog_modify_user_info.xml,里面的代码如下:
<?xml version="1.0" encoding="utf-8"?><LinearLayout
android:layout_width=“330dp”
android:layout_height=“wrap_content”
android:background=“@drawable/shape_bg_white_radius_24”
android:orientation=“vertical”>
<TextView
android:layout_width=“match_parent”
android:layout_height=“wrap_content”
android:gravity=“center”
android:padding=“12dp”
android:text=“修改用户信息”
android:textColor=“@color/purple_500”
android:textSize=“16sp” />
<View
android:layout_width=“match_parent”
android:layout_height=“1dp”
android:background=“@color/line” />
<TextView
android:id=“@+id/tv_modify_avatar”
android:layout_width=“match_parent”
android:layout_height=“wrap_content”
android:foreground=“?selectableItemBackground”
android:gravity=“center”
android:padding=“12dp”
android:text=“修改头像”
android:textColor=“@color/black”
android:textSize=“16sp” />
<LinearLayout
android:id=“@+id/lay_modify_avatar”
android:layout_width=“match_parent”
android:layout_height=“wrap_content”
android:orientation=“vertical”
android:visibility=“gone”>
<TextView
android:id=“@+id/tv_album_selection”
android:layout_width=“match_parent”
android:layout_height=“wrap_content”
android:background=“@color/line”
android:foreground=“?selectableItemBackground”
android:gravity=“center”
android:padding=“12dp”
android:text=“相册选择”
android:textColor=“@color/black”
android:textSize=“16sp” />
<TextView
android:id=“@+id/tv_camera_photo”
android:layout_width=“match_parent”
android:layout_height=“wrap_content”
android:background=“@color/line”
android:foreground=“?selectableItemBackground”
android:gravity=“center”
android:padding=“12dp”
android:text=“相机拍照”
android:textColor=“@color/black”
android:textSize=“16sp” />
<View
android:layout_width=“match_parent”
android:layout_height=“0.5dp”
android:background=“@color/line” />
<TextView
android:id=“@+id/tv_modify_nickname”
android:layout_width=“match_parent”
android:layout_height=“wrap_content”
android:foreground=“?selectableItemBackground”
android:gravity=“center”
android:padding=“12dp”
android:text=“修改昵称”
android:textColor=“@color/black”
android:textSize=“16sp” />
<View
android:layout_width=“match_parent”
android:layout_height=“0.5dp”
android:background=“@color/line” />
<TextView
android:id=“@+id/tv_modify_Introduction”
android:layout_width=“match_parent”
android:layout_height=“wrap_content”
android:foreground=“?selectableItemBackground”
android:gravity=“center”
android:padding=“12dp”
android:text=“修改简介”
android:textColor=“@color/black”
android:textSize=“16sp” />
<View
android:layout_width=“match_parent”
android:layout_height=“0.5dp”
android:background=“@color/line” />
<TextView
android:id=“@+id/tv_close”
android:layout_width=“match_parent”
android:layout_height=“wrap_content”
android:gravity=“center”
android:padding=“12dp”
android:text=“关闭”
android:textColor=“@color/purple_500”
android:textSize=“16sp” />
这里的准备工作就都做好了,后面会用到,先不着急,然后在BaseActivity中增加一个加载弹窗,
private LoadingDialog loadingDialog;
/**
- 显示加载弹窗
*/
protected void showLoading() {
loadingDialog = new LoadingDialog(this);
loadingDialog.show();
}
/**
-
显示加载弹窗
-
@param isClose true 则点击其他区域弹窗关闭, false 不关闭。
*/
protected void showLoading(boolean isClose) {
loadingDialog = new LoadingDialog(this, isClose);
loadingDialog.show();
}
/**
- 隐藏加载弹窗
*/
protected void dismissLoading() {
if (loadingDialog != null) {
loadingDialog.dismiss();
}
}
这样在Activity中就可以直接使用,显示加载弹窗,隐藏加载弹窗。
权限在Android上是一个麻烦但是又不得不做的事情,如果你要是还是Android6.0以下的手机就可以不用管这些,但是很可惜现在都是Android10,11了,因此我们还需要做兼容。
① 权限配置
因为要用到文件读写和相机,所以就需要在AndroidManifest.xml中增加如下代码:
<uses-permission
android:name=“android.permission.WRITE_EXTERNAL_STORAGE”
tools:ignore=“ScopedStorage” />
<uses-permission
android:name=“android.permission.MANAGE_EXTERNAL_STORAGE”
tools:ignore=“ScopedStorage” />
这还没有完的,在Android10.0上要访问文件,需要在application比前中添加
android:requestLegacyExternalStorage=“true”
如下图所示:
同事我们还需要兼容Android7.0,在xml文件夹下新建一个file_paths.xml,里面的代码如下:
<?xml version="1.0" encoding="utf-8"?><external-files-path
name=“images”
path=“Pictures” />
然后我们在AndroidManifest.xml中配置它,代码如下:
<provider
android:name=“androidx.core.content.FileProvider”
android:authorities=“${applicationId}.fileprovider”
android:exported=“false”
android:grantUriPermissions=“true”>
<meta-data
android:name=“android.support.FILE_PROVIDER_PATHS”
android:resource=“@xml/file_paths” />
添加位置如下:
② 权限工具类
我这里可以自己写一个工具类,当然也可以用第三方框架,在utils包下新建一个PermissionUtils类,里面的代码如下:
public class PermissionUtils {
private static PermissionUtils mInstance;
public static final String READ_EXTERNAL_STORAGE = Manifest.permission.READ_EXTERNAL_STORAGE;
public static final String WRITE_EXTERNAL_STORAGE = Manifest.permission.WRITE_EXTERNAL_STORAGE;
public static final String CAMERA = Manifest.permission.CAMERA;
public static final int REQUEST_STORAGE_CODE = 1001;
public static final int REQUEST_CAMERA_CODE = 1002;
public static final int REQUEST_MANAGE_EXTERNAL_STORAGE_CODE = 1000;
public static PermissionUtils getInstance() {
if (mInstance == null) {
synchronized (PermissionUtils.class) {
if (mInstance == null) {
mInstance = new PermissionUtils();
}
}
}
return mInstance;
}
/**
-
检查是有拥有某权限
-
@param permission 权限名称
-
@return true 有 false 没有
*/
public static boolean hasPermission(Activity activity, String permission) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
return activity.checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED;
} else {
return true;
}
}
/**
-
通过权限名称获取请求码
-
@param permissionName 权限名称
-
@return requestCode 权限请求码
*/
private static int getPermissionRequestCode(String permissionName) {
int requestCode;
switch (permissionName) {
case READ_EXTERNAL_STORAGE:
case WRITE_EXTERNAL_STORAGE:
requestCode = REQUEST_STORAGE_CODE;
break;
case CAMERA:
requestCode = REQUEST_CAMERA_CODE;
break;
default:
requestCode = 1000;
break;
}
return requestCode;
}
/**
-
请求权限
-
@param permission 权限名称
*/
public static void requestPermission(Activity activity, String permission) {
int requestCode = getPermissionRequestCode(permission);
//请求此权限
ActivityCompat.requestPermissions(activity, new String[]{permission}, requestCode);
}
}
然后因为权限请求是Activity有关系,那么我们可以在BaseActivity中再封装一层,
/**
- 打开相册请求码
*/
protected static final int SELECT_PHOTO_CODE = 2000;
/**
- 打开相机请求码
*/
protected static final int TAKE_PHOTO_CODE = 2001;
添加两个请求吗,因为打开相机和相册都需要跳转到系统的页面,还需要获取返回的数据,这里我就提前定义好,然后在onCreate中对PermissionUtils进行初始化。
在BaseActivity中添加如下代码:
/**
- 当前是否在Android11.0及以上
*/
protected boolean isAndroid11() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.R;
}
/**
- 当前是否在Android10.0及以上
*/
protected boolean isAndroid10() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q;
}
/**
- 当前是否在Android7.0及以上
*/
protected boolean isAndroid7() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N;
}
/**
- 当前是否在Android6.0及以上
*/
protected boolean isAndroid6() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;
}
protected boolean isStorageManager() {
return Environment.isExternalStorageManager();
}
protected boolean hasPermission(String permissionName) {
return PermissionUtils.hasPermission(this, permissionName);
}
protected void requestPermission(String permissionName) {
PermissionUtils.requestPermission(this, permissionName);
}
/**
- 请求外部存储管理 Android11版本时获取文件读写权限时调用
*/
protected void requestManageExternalStorage() {
Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION);
intent.setData(Uri.parse(“package:” + getPackageName()));
startActivityForResult(intent, PermissionUtils.REQUEST_MANAGE_EXTERNAL_STORAGE_CODE);
}
定义了一些需要用到的方法。下面进行DataBinding使用,弹窗中怎么获取DataBinding。
首先在activity_home.xml中添加 , 代码如下:
<variable
name=“homeViewModel”
type=“com.llw.mvvm.viewmodels.HomeViewModel” />
然后修改主页面的头像数据DataBinding,代码如下:
<com.llw.mvvm.view.CustomImageView
android:id=“@+id/iv_avatar”
localUrl=“@{homeViewModel.user.avatar}”
android:layout_width=“36dp”
android:layout_height=“36dp”
android:padding=“0.5dp”
android:scaleType=“centerCrop”
android:src=“@drawable/logo”
app:shapeAppearanceOverlay=“@style/circleImageStyle”
app:strokeColor=“@color/white”
app:strokeWidth=“1dp” />
这里的localUrl需要我们再去CustomImageView类中定义,在CustomImageView中添加如下代码:
private static final RequestOptions OPTIONS_LOCAL = new RequestOptions()
.placeholder(R.drawable.logo)//图片加载出来前,显示的图片
.fallback(R.drawable.logo) //url为空的时候,显示的图片
.error(R.mipmap.ic_loading_failed)//图片加载失败后,显示的图片
.diskCacheStrategy(DiskCacheStrategy.NONE)//不做磁盘缓存
.skipMemoryCache(true);
@BindingAdapter(value = {“localUrl”}, requireAll = false)
public static void setLocalUrl(ImageView imageView, String url) {
Glide.with(BaseApplication.getContext()).load(url).apply(OPTIONS_LOCAL).into(imageView);
}
然后就是在nav_header.xml中绑定DataBinding,里面的代码如下:
<?xml version="1.0" encoding="utf-8"?><layout xmlns:android=“http://schemas.android.com/apk/res/android”
xmlns:app=“http://schemas.android.com/apk/res-auto”>
<variable
name=“homeViewModel”
type=“com.llw.mvvm.viewmodels.HomeViewModel” />
<LinearLayout
android:layout_width=“match_parent”
android:layout_height=“wrap_content”
android:orientation=“vertical”>
<RelativeLayout
android:id=“@+id/lay_user_info”
android:layout_width=“match_parent”
android:layout_height=“120dp”
android:background=“@color/purple_500”>
<com.llw.mvvm.view.CustomImageView
android:id=“@+id/iv_avatar”
localUrl=“@{homeViewModel.user.avatar}”
android:layout_width=“80dp”
android:layout_height=“80dp”
android:layout_centerVertical=“true”
android:layout_marginStart=“24dp”
android:layout_marginEnd=“24dp”
android:padding=“1dp”
android:scaleType=“centerCrop”
android:src=“@drawable/logo”
app:shapeAppearanceOverlay=“@style/circleImageStyle”
app:strokeColor=“@color/white”
app:strokeWidth=“2dp” />
<TextView
android:id=“@+id/tv_name”
android:layout_width=“wrap_content”
android:layout_height=“wrap_content”
android:layout_alignTop=“@+id/iv_avatar”
android:layout_marginTop=“16dp”
android:layout_toEndOf=“@+id/iv_avatar”
android:text=“@{homeViewModel.user.nickname ?? homeViewModel.defaultName}”
android:textColor=“#FFF”
android:textSize=“16sp” />
<TextView
android:id=“@+id/tv_tip”
android:layout_width=“wrap_content”
android:layout_height=“wrap_content”
android:layout_below=“@+id/tv_name”
android:layout_marginTop=“8dp”
android:layout_toEndOf=“@+id/iv_avatar”
android:text=“@{homeViewModel.user.introduction ?? homeViewModel.defaultIntroduction}”
android:textColor=“#FFF”
android:textSize=“14sp” />
这里面的这一行代码需要说一下
homeViewModel.user.nickname ?? homeViewModel.defaultName
这一行代码就等同于
homeViewModel.user.nickname != null ? homeViewModel.user.nickname : homeViewModel.defaultName
这个defaultName是我前面设置的默认值,因为注册时可能不会填写昵称和简介。这里要让这个默认值起作用,在保存用户信息的使用。这里需要修改注册页面中的默认值,从之前的空字符串改成null,这样在xml中的判断值才会有作用,同时及时你的值为null,在xml中也不会报错,这是DataBinding做了处理,类似于Kotlin中的空安全。
这里的DataBinding主要实现两个功能,第一个是HomeActivity的标题栏头像能够根据用户修改图片变化而变化,没有修改则使用默认的头像,第二个就是NavigationView中的head_layout也是通过用户手动去修改昵称、简介、头像时发生变化。
很快就要进入主要内容了,在代码中我们经常会用到一些工具类,比如dp转px,时间处理、Bitmp处理,相机图片处理,鉴于在后面我将会用到这些工具类,现在就给贴出来。这里的工具类都放在utils包下面,新建SizeUtils类,代码如下:
public final class SizeUtils {
private SizeUtils() {
throw new UnsupportedOperationException(“u can’t instantiate me…”);
}
/**
-
Value of dp to value of px.
-
@param dpValue The value of dp.
-
@return value of px
*/
public static int dp2px(Context context, final float dpValue) {
final float scale = context.getApplicationContext().getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
/**
-
Value of px to value of dp.
-
@param pxValue The value of px.
-
@return value of dp
*/
public static int px2dp(Context context, final float pxValue) {
final float scale = context.getApplicationContext().getResources().getDisplayMetrics().density;
return (int) (pxValue / scale + 0.5f);
}
/**
-
Value of sp to value of px.
-
@param spValue The value of sp.
-
@return value of px
*/
public static int sp2px(Context context, final float spValue) {
final float fontScale = context.getApplicationContext().getResources().getDisplayMetrics().scaledDensity;
return (int) (spValue * fontScale + 0.5f);
}
/**
-
Value of px to value of sp.
-
@param pxValue The value of px.
-
@return value of sp
*/
public static int px2sp(Context context, final float pxValue) {
final float fontScale = context.getApplicationContext().getResources().getDisplayMetrics().scaledDensity;
return (int) (pxValue / fontScale + 0.5f);
}
/**
-
Converts an unpacked complex data value holding a dimension to its final floating
-
point value. The two parameters unit and value
-
are as in {@link TypedValue#TYPE_DIMENSION}.
-
@param value The value to apply the unit to.
-
@param unit The unit to convert from.
-
@return The complex floating point value multiplied by the appropriate
-
metrics depending on its unit.
*/
public static float applyDimension(Context context, final float value, final int unit) {
DisplayMetrics metrics = context.getApplicationContext().getResources().getDisplayMetrics();
switch (unit) {
case TypedValue.COMPLEX_UNIT_PX:
return value;
case TypedValue.COMPLEX_UNIT_DIP:
return value * metrics.density;
case TypedValue.COMPLEX_UNIT_SP:
return value * metrics.scaledDensity;
case TypedValue.COMPLEX_UNIT_PT:
return value * metrics.xdpi * (1.0f / 72);
case TypedValue.COMPLEX_UNIT_IN:
return value * metrics.xdpi;
case TypedValue.COMPLEX_UNIT_MM:
return value * metrics.xdpi * (1.0f / 25.4f);
}
return 0;
}
/**
-
Force get the size of view.
-
e.g.
-
SizeUtils.forceGetViewSize(view, new SizeUtils.onGetSizeListener() {
-
Override
-
public void onGetSize(final View view) {
-
view.getWidth();
-
}
-
});
-
@param view The view.
-
@param listener The get size listener.
*/
public static void forceGetViewSize(final View view, final onGetSizeListener listener) {
view.post(new Runnable() {
@Override
public void run() {
if (listener != null) {
listener.onGetSize(view);
}
}
});
}
/**
-
Return the width of view.
-
@param view The view.
-
@return the width of view
*/
public static int getMeasuredWidth(final View view) {
return measureView(view)[0];
}
/**
-
Return the height of view.
-
@param view The view.
-
@return the height of view
*/
public static int getMeasuredHeight(final View view) {
return measureView(view)[1];
}
/**
-
Measure the view.
-
@param view The view.
-
@return arr[0]: view’s width, arr[1]: view’s height
*/
public static int[] measureView(final View view) {
ViewGroup.LayoutParams lp = view.getLayoutParams();
if (lp == null) {
lp = new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
);
}
int widthSpec = ViewGroup.getChildMeasureSpec(0, 0, lp.width);
int lpHeight = lp.height;
int heightSpec;
if (lpHeight > 0) {
heightSpec = View.MeasureSpec.makeMeasureSpec(lpHeight, View.MeasureSpec.EXACTLY);
} else {
heightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
}
view.measure(widthSpec, heightSpec);
return new int[]{view.getMeasuredWidth(), view.getMeasuredHeight()};
}
public interface onGetSizeListener {
void onGetSize(View view);
}
}
EasyDate类,代码如下:
public final class EasyDate {
public static final String STANDARD_TIME = “yyyy-MM-dd HH:mm:ss”;
public static final String FULL_TIME = “yyyy-MM-dd HH:mm:ss.SSS”;
public static final String YEAR_MONTH_DAY = “yyyy-MM-dd”;
public static final String YEAR_MONTH_DAY_CN = “yyyy年MM月dd号”;
public static final String HOUR_MINUTE_SECOND = “HH:mm:ss”;
public static final String HOUR_MINUTE_SECOND_CN = “HH时mm分ss秒”;
public static final String YEAR = “yyyy”;
public static final String MONTH = “MM”;
public static final String DAY = “dd”;
public static final String HOUR = “HH”;
public static final String MINUTE = “mm”;
public static final String SECOND = “ss”;
public static final String MILLISECOND = “SSS”;
public static final String YESTERDAY = “昨天”;
public static final String TODAY = “今天”;
public static final String TOMORROW = “明天”;
public static final String SUNDAY = “星期日”;
public static final String MONDAY = “星期一”;
public static final String TUESDAY = “星期二”;
public static final String WEDNESDAY = “星期三”;
public static final String THURSDAY = “星期四”;
public static final String FRIDAY = “星期五”;
public static final String SATURDAY = “星期六”;
public static final String[] weekDays = {SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY};
/**
-
获取标准时间
-
@return 例如 2021-07-01 10:35:53
*/
public static String getDateTime() {
return new SimpleDateFormat(STANDARD_TIME, Locale.CHINESE).format(new Date());
}
/**
-
获取完整时间
-
@return 例如 2021-07-01 10:37:00.748
*/
public static String getFullDateTime() {
return new SimpleDateFormat(FULL_TIME, Locale.CHINESE).format(new Date());
}
/**
-
获取年月日(今天)
-
@return 例如 2021-07-01
*/
public static String getTheYearMonthAndDay() {
return new SimpleDateFormat(YEAR_MONTH_DAY, Locale.CHINESE).format(new Date());
}
/**
-
获取年月日
-
@return 例如 2021年07月01号
*/
public static String getTheYearMonthAndDayCn() {
return new SimpleDateFormat(YEAR_MONTH_DAY_CN, Locale.CHINESE).format(new Date());
}
/**
-
获取年月日
-
@param delimiter 分隔符
-
@return 例如 2021年07月01号
*/
public static String getTheYearMonthAndDayDelimiter(CharSequence delimiter) {
return new SimpleDateFormat(YEAR + delimiter + MONTH + delimiter + DAY, Locale.CHINESE).format(new Date());
}
/**
-
获取时分秒
-
@return 例如 10:38:25
*/
public static String getHoursMinutesAndSeconds() {
return new SimpleDateFormat(HOUR_MINUTE_SECOND, Locale.CHINESE).format(new Date());
}
/**
-
获取时分秒
-
@return 例如 10时38分50秒
*/
public static String getHoursMinutesAndSecondsCn() {
return new SimpleDateFormat(HOUR_MINUTE_SECOND_CN, Locale.CHINESE).format(new Date());
}
/**
-
获取时分秒
-
@param delimiter 分隔符
-
@return 例如 2021/07/01
*/
public static String getHoursMinutesAndSecondsDelimiter(CharSequence delimiter) {
return new SimpleDateFormat(HOUR + delimiter + MINUTE + delimiter + SECOND, Locale.CHINESE).format(new Date());
}
/**
-
获取年
-
@return 例如 2021
*/
public static String getYear() {
return new SimpleDateFormat(YEAR, Locale.CHINESE).format(new Date());
}
/**
-
获取月
-
@return 例如 07
*/
public static String getMonth() {
return new SimpleDateFormat(MONTH, Locale.CHINESE).format(new Date());
}
/**
-
获取天
-
@return 例如 01
*/
public static String getDay() {
return new SimpleDateFormat(DAY, Locale.CHINESE).format(new Date());
}
/**
-
获取小时
-
@return 例如 10
*/
public static String getHour() {
return new SimpleDateFormat(HOUR, Locale.CHINESE).format(new Date());
}
/**
-
获取分钟
-
@return 例如 40
*/
public static String getMinute() {
return new SimpleDateFormat(MINUTE, Locale.CHINESE).format(new Date());
}
/**
-
获取秒
-
@return 例如 58
*/
public static String getSecond() {
return new SimpleDateFormat(SECOND, Locale.CHINESE).format(new Date());
}
/**
-
获取毫秒
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
最后
我见过很多技术leader在面试的时候,遇到处于迷茫期的大龄程序员,比面试官年龄都大。这些人有一些共同特征:可能工作了5、6年,还是每天重复给业务部门写代码,工作内容的重复性比较高,没有什么技术含量的工作。问到这些人的职业规划时,他们也没有太多想法。
其实30岁到40岁是一个人职业发展的黄金阶段,一定要在业务范围内的扩张,技术广度和深度提升上有自己的计划,才有助于在职业发展上有持续的发展路径,而不至于停滞不前。
不断奔跑,你就知道学习的意义所在!
一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
r(CharSequence delimiter) {
return new SimpleDateFormat(YEAR + delimiter + MONTH + delimiter + DAY, Locale.CHINESE).format(new Date());
}
/**
-
获取时分秒
-
@return 例如 10:38:25
*/
public static String getHoursMinutesAndSeconds() {
return new SimpleDateFormat(HOUR_MINUTE_SECOND, Locale.CHINESE).format(new Date());
}
/**
-
获取时分秒
-
@return 例如 10时38分50秒
*/
public static String getHoursMinutesAndSecondsCn() {
return new SimpleDateFormat(HOUR_MINUTE_SECOND_CN, Locale.CHINESE).format(new Date());
}
/**
-
获取时分秒
-
@param delimiter 分隔符
-
@return 例如 2021/07/01
*/
public static String getHoursMinutesAndSecondsDelimiter(CharSequence delimiter) {
return new SimpleDateFormat(HOUR + delimiter + MINUTE + delimiter + SECOND, Locale.CHINESE).format(new Date());
}
/**
-
获取年
-
@return 例如 2021
*/
public static String getYear() {
return new SimpleDateFormat(YEAR, Locale.CHINESE).format(new Date());
}
/**
-
获取月
-
@return 例如 07
*/
public static String getMonth() {
return new SimpleDateFormat(MONTH, Locale.CHINESE).format(new Date());
}
/**
-
获取天
-
@return 例如 01
*/
public static String getDay() {
return new SimpleDateFormat(DAY, Locale.CHINESE).format(new Date());
}
/**
-
获取小时
-
@return 例如 10
*/
public static String getHour() {
return new SimpleDateFormat(HOUR, Locale.CHINESE).format(new Date());
}
/**
-
获取分钟
-
@return 例如 40
*/
public static String getMinute() {
return new SimpleDateFormat(MINUTE, Locale.CHINESE).format(new Date());
}
/**
-
获取秒
-
@return 例如 58
*/
public static String getSecond() {
return new SimpleDateFormat(SECOND, Locale.CHINESE).format(new Date());
}
/**
-
获取毫秒
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-oma653mZ-1712767782308)]
[外链图片转存中…(img-OfCHspM4-1712767782309)]
[外链图片转存中…(img-GnOeklzG-1712767782309)]
[外链图片转存中…(img-fCDL6EIO-1712767782309)]
[外链图片转存中…(img-j8tpkBKS-1712767782310)]
[外链图片转存中…(img-bmSmOhn6-1712767782310)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
[外链图片转存中…(img-3JN0FXcP-1712767782310)]
最后
我见过很多技术leader在面试的时候,遇到处于迷茫期的大龄程序员,比面试官年龄都大。这些人有一些共同特征:可能工作了5、6年,还是每天重复给业务部门写代码,工作内容的重复性比较高,没有什么技术含量的工作。问到这些人的职业规划时,他们也没有太多想法。
其实30岁到40岁是一个人职业发展的黄金阶段,一定要在业务范围内的扩张,技术广度和深度提升上有自己的计划,才有助于在职业发展上有持续的发展路径,而不至于停滞不前。
不断奔跑,你就知道学习的意义所在!
[外链图片转存中…(img-5qED3HIl-1712767782310)]
[外链图片转存中…(img-zDkoZ75H-1712767782311)]
一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
[外链图片转存中…(img-ZSGHwqLY-1712767782311)]