手把手教你擼一個Loading
阿新 • • 發佈:2018-11-19
作為 Android 開發者,無奈經常會碰到各種各樣的奇葩需求,現在大多公司 UI 設計圖、標註都是按 IOS 來設計的,包括一個IOS特有的效果等,要實現和 IOS 一樣的效果,無奈 Android 只能各種仿 IOS 了,經常也是產品被懟,IOS 能實現,為什麼 Android 不能實現?好吧,今天我們就來寫一個仿 IOS 的載入 loading 效果。
1.先看效果圖
效果還滿意吧?實現的思路是,在頁面上彈出一個全屏的 popupWindow,居中是一個半透明的圓角shape,中間一個 gif 圖片,下面是一個 TextView,最外層佈局背景為透明,另外把其他需外設定的屬性及引數通過建造者模式從外部進行配置,同時提供預設的屬性值,這樣可以適應各種需求的變化了,程式碼也比較簡單,我的註釋也寫的比較詳細,相信你一定能看得懂。
2.程式碼實現
/**
* Created by x-sir on 2018/8/22 :)
* Function:
*/
public class LoadingView {
private String mText;
private int mTextSize;
private int mGifWidth;
private int mGifHeight;
private int mDrawableId;
private View mPopupView;
private Context mContext;
private String mTextColor;
private int mCornerRadius;
private int mLoadingWidth;
private int mLoadingHeight;
private int mTextMarginTop;
private boolean mIsFocusable;
private String mLoadingBgColor;
private PopupWindow mPopupWindow;
private WeakReference<View> mView;
private OnLoadingListener mListener;
private static final String DEFAULT_TEXT = "載入中..."; // default text
private static final int DEFAULT_TEXT_SIZE = 12; // default text size
private static final int DEFAULT_TEXT_MARGIN_TOP = 6; // default text margin top
private static final String DEFAULT_TEXT_COLOR = "#FFFFFF"; // default text color
private static final int DEFAULT_CORNER_RADIUS = 4; // default loading background radius size
private static final String DEFAULT_LOADING_BG_COLOR = "#CC000000"; // default loading background color
private static final int DEFAULT_DRAWABLE_ID = R.drawable.loading1; // default loading drawable
private static final int DEFAULT_GIF_WIDTH = 30; // default gif width
private static final int DEFAULT_GIF_HEIGHT = 30; // default gif height
/**
* Constructor.
*
* @param builder
*/
public LoadingView(Builder builder) {
this.mText = builder.text;
this.mView = builder.view;
this.mListener = builder.listener;
this.mTextSize = builder.textSize;
this.mTextColor = builder.textColor;
this.mCornerRadius = builder.cornerRadius;
this.mContext = builder.applicationContext;
this.mLoadingBgColor = builder.loadingBgColor;
this.mDrawableId = builder.drawableId;
this.mGifWidth = builder.gifWidth;
this.mGifHeight = builder.gifHeight;
this.mLoadingWidth = builder.loadingWidth;
this.mLoadingHeight = builder.loadingHeight;
this.mTextMarginTop = builder.textMarginTop;
this.mIsFocusable = builder.isFocusable;
initView();
}
/**
* Initialize view parameters.
*/
private void initView() {
if (mPopupView == null) {
mPopupView = View.inflate(mContext, R.layout.popupwindow_loading, null);
}
if (mPopupWindow == null) {
mPopupWindow = new PopupWindow(mPopupView, WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.MATCH_PARENT);
}
mPopupWindow.setOnDismissListener(() -> {
if (mListener != null) {
mListener.onDismiss();
}
});
mPopupWindow.setBackgroundDrawable(new BitmapDrawable());
// 當 mIsFocusable 為 true 時,響應返回鍵消失,為 false 時響應 activity 返回操作,預設為 false
mPopupWindow.setFocusable(mIsFocusable);
LinearLayout llLoadingBg = (LinearLayout) mPopupView.findViewById(R.id.llLoadingBg);
ImageView ivLoading = (ImageView) mPopupView.findViewById(R.id.ivLoading);
TextView tvContent = (TextView) mPopupView.findViewById(R.id.tvContent);
RelativeLayout.LayoutParams rlParams = (RelativeLayout.LayoutParams) llLoadingBg.getLayoutParams();
if (mLoadingWidth != -1 && mLoadingHeight != -1) {
rlParams.width = dp2px(mLoadingWidth);
rlParams.height = dp2px(mLoadingHeight);
} else {
rlParams.width = RelativeLayout.LayoutParams.WRAP_CONTENT;
rlParams.height = RelativeLayout.LayoutParams.WRAP_CONTENT;
}
llLoadingBg.setLayoutParams(rlParams);
GradientDrawable mGroupDrawable = new GradientDrawable();
/*設定 Drawable 的形狀為矩形*/
mGroupDrawable.setShape(GradientDrawable.RECTANGLE);
/*設定背景顏色*/
mGroupDrawable.setColor(Color.parseColor(mLoadingBgColor));
/*設定圓角大小*/
mGroupDrawable.setCornerRadius(dp2px(mCornerRadius));
llLoadingBg.setBackground(mGroupDrawable);
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) tvContent.getLayoutParams();
params.topMargin = dp2px(mTextMarginTop);
tvContent.setLayoutParams(params);
/*設定顯示文字*/
tvContent.setText(mText);
/*設定文字大小(以 SP 為單位)*/
tvContent.setTextSize(TypedValue.COMPLEX_UNIT_SP, mTextSize);
/*設定文字顏色*/
tvContent.setTextColor(Color.parseColor(mTextColor));
LinearLayout.LayoutParams llParams = (LinearLayout.LayoutParams) ivLoading.getLayoutParams();
llParams.width = dp2px(mGifWidth);
llParams.height = dp2px(mGifHeight);
ivLoading.setLayoutParams(llParams);
/*載入 GIF 圖片*/
Glide.with(mContext).load(mDrawableId)
.diskCacheStrategy(DiskCacheStrategy.SOURCE)
.into(ivLoading);
}
/**
* Show popupWindow.
*/
public void show() {
dismiss();
if (mPopupWindow != null) {
// 必須要 post runnable,如果在onCreate中呼叫則會拋:android.view.WindowManager$BadTokenException: Unable to add window -- token
mView.get().post(() -> mPopupWindow.showAtLocation(mView.get(),
Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, 0, 0));
}
}
/**
* Cancel popupWindow showing.
*/
public void dismiss() {
if (mPopupWindow != null && mPopupWindow.isShowing()) {
mPopupWindow.dismiss();
}
}
/**
* Invoke on Activity onDestroy() method.
*/
public void dispose() {
if (mPopupWindow != null && mPopupWindow.isShowing()) {
mPopupWindow.dismiss();
}
mPopupWindow = null;
if (mView != null) {
mView.clear();
mView = null;
}
}
/**
* PopupWindow is or not showing.
*
* @return
*/
public boolean isShowing() {
return mPopupWindow != null && mPopupWindow.isShowing();
}
/**
* Builder inner class.
*/
public static final class Builder {
private String text;
private String textColor;
private int textSize = -1;
private int gifWidth = -1;
private int gifHeight = -1;
private int drawableId = -1;
private String loadingBgColor;
private int cornerRadius = -1;
private int loadingWidth = -1;
private int loadingHeight = -1;
private int textMarginTop = -1;
private boolean isFocusable = false;
private WeakReference<View> view;
private OnLoadingListener listener;
private Context applicationContext;
/**
* Constructor
*/
public Builder(Context context) {
this.applicationContext = context.getApplicationContext();
}
/**
* Set content text.
*
* @param text
* @return
*/
public Builder setText(String text) {
this.text = text;
return this;
}
/**
* Set text size.
*
* @param textSize
* @return
*/
public Builder setTextSize(int textSize) {
this.textSize = textSize;
return this;
}
/**
* Set text margin top dimen.
*
* @param textMarginTop
* @return
*/
public Builder setTextMarginTop(int textMarginTop) {
this.textMarginTop = textMarginTop;
return this;
}
/**
* Set popupWindow's focusable.
*
* @param isFocusable
* @return
*/
public Builder setFocusable(boolean isFocusable) {
this.isFocusable = isFocusable;
return this;
}
/**
* Set gif imageView width.
*
* @param gifWidth
* @return
*/
public Builder setGifWidth(int gifWidth) {
this.gifWidth = gifWidth;
return this;
}
/**
* Set gif imageView height.
*
* @param gifHeight
* @return
*/
public Builder setGifHeight(int gifHeight) {
this.gifHeight = gifHeight;
return this;
}
/**
* Set gif loadingView width.
*
* @param loadingWidth
* @return
*/
public Builder setLoadingWidth(int loadingWidth) {
this.loadingWidth = loadingWidth;
return this;
}
/**
* Set gif loadingView height.
*
* @param loadingHeight
* @return
*/
public Builder setLoadingHeight(int loadingHeight) {
this.loadingHeight = loadingHeight;
return this;
}
/**
* Set text color.
*
* @param textColor
* @return
*/
public Builder setTextColor(String textColor) {
this.textColor = textColor;
return this;
}
/**
* Set loadingView corner radius.
*
* @param cornerRadius
* @return
*/
public Builder setCornerRadius(int cornerRadius) {
this.cornerRadius = cornerRadius;
return this;
}
/**
* Set loadingView background color.
*
* @param loadingBgColor
* @return
*/
public Builder setLoadingBgColor(String loadingBgColor) {
this.loadingBgColor = loadingBgColor;
return this;
}
/**
* Set gif drawable resource.
*
* @param drawableId
* @return
*/
public Builder setGifDrawable(int drawableId) {
this.drawableId = drawableId;
return this;
}
/**
* Set location at parent view, because popupWindow must be dependency activity.
*
* @param view
* @return
*/
public Builder setDropView(View view) {
if (view != null) {
this.view = new WeakReference<>(view);
} else {
throw new IllegalArgumentException("must be point parent view!");
}
return this;
}
/**
* set on popupWindow dismiss listener.
*
* @param listener
* @return
*/
public Builder setListener(OnLoadingListener listener) {
this.listener = listener;
return this;
}
public LoadingView build() {
if (TextUtils.isEmpty(text)) {
text = DEFAULT_TEXT;
}
if (textSize == -1) {
textSize = DEFAULT_TEXT_SIZE;
}
if (textMarginTop == -1) {
textMarginTop = DEFAULT_TEXT_MARGIN_TOP;
}
if (TextUtils.isEmpty(textColor)) {
textColor = DEFAULT_TEXT_COLOR;
}
if (TextUtils.isEmpty(loadingBgColor)) {
loadingBgColor = DEFAULT_LOADING_BG_COLOR;
}
if (cornerRadius == -1) {
cornerRadius = DEFAULT_CORNER_RADIUS;
}
if (view == null) {
throw new IllegalArgumentException("must be point parent view!");
}
if (drawableId == -1) {
drawableId = DEFAULT_DRAWABLE_ID;
}
if (gifWidth == -1) {
gifWidth = DEFAULT_GIF_WIDTH;
}
if (gifHeight == -1) {
gifHeight = DEFAULT_GIF_HEIGHT;
}
return new LoadingView(this);
}
}
/**
* dp convert to px.
*
* @param dpValue
* @return
*/
private int dp2px(float dpValue) {
float scale = mContext.getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
/**
* Define popupWindow dismiss listener.
*/
interface OnLoadingListener {
void onDismiss();
}
}
3.用法
3.1初始化Loading
1.最基本的用法:
// 因為 PopupWindow 依賴於Activity,所以必須要呼叫 setDropView 方法設定要掛載的 View,
// 一般是 Activity 或 Fragment 的根 View,其他引數可根據需求進行設定。
mLoadingView = new LoadingView.Builder(this)
.setDropView(activity_main)
.build();
2.自定義設定各種引數:
mLoadingView = new LoadingView.Builder(this)
.setText("拼命載入中...") // 設定文案
.setTextSize(12) // 設定字型大小(sp)
.setTextColor("#FFFFFF") // 設定字型顏色(#RGB & #ARGB)
.setTextMarginTop(10) // 設定文字距上的距離(dp)
.setCornerRadius(4) // 設定圓角半徑(dp)
.setLoadingBgColor("#CC000000") // 設定背景顏色(#RGB & #ARGB)
.setLoadingWidth(120) // 設定 loading 的寬(dp)
.setLoadingHeight(100) // 設定 loading 的高(dp)
.setListener(listener) // 設定監聽
.setDropView(activity_main) // 設定要掛載的 View(必須要設定),一般是 Activity 或 Fragment 的根 View
.setGifDrawable(R.drawable.loading4) // 設定 gif 資源
.setFocusable(false) // 為 true 時,響應返回鍵消失,為 false 時響應 activity 返回操作,預設為 false
.setGifWidth(16) // 設定 gif 的寬(dp)
.setGifHeight(16) // 設定 gif 的高(dp)
.build();
3.2 顯示Loading
mLoadingView.show();
3.3 取消Loading
mLoadingView.dismiss();
4.支援的自定義設定
- 支援設定字型文案、及顏色和字型的大小;
- 支援設定文字距上的邊距;
- 支援設定Loading的寬高;
- 支援設定Loading的圓角半徑及背景顏色;
- 支援設定Loading的監聽;
- 支援設定載入其他gif資源;
- 支援設定gif圖片顯示的寬高;
- 支援設定Loading的焦點;
- …
好了,今天的分享就到這裡,需要下載 Demo 的請點選 「閱讀原文」!
猜你喜歡:
深入淺出Retrofit2.x(一)
深入淺出Retrofit2.x(二)
不得不會的10點Java基礎知識
Android 最全 Intent 傳遞資料姿勢