转载请注明出处:
http://blog.csdn.net/aa464971/article/details/70197394
已经迁移到新版,请移步
http://blog.csdn.net/aa464971/article/details/70197394
源码与例子下载地址:
https://github.com/xiandanin/LoadingBar
前言
我们平时在开发的时候,发起网络请求前,会需要显示一个Loading,一般的做法都是在xml布局上添加好Loading,然后在Activity中,setVisibility来控制Loading的显示和隐藏,这样使用起来就很不方便,因为每一个xml都得引入一个Loading布局。
而LoadingBar就更好的解决了这个问题
先来看看效果是怎么样的
使用方法
先在build.gradle中添加一个引入
compile 'com.dyhdyh.loadingbar:loadingbar:1.3'
再来看看这个页面的布局是怎么写的
其中的FrameLayout就是你要显示的内容,如果你把这个FrameLayout传给LoadingBar,LoadingBar会覆盖在内容上面,从而达到Loading的效果
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.dyhdyh.loadingbar.example.MainActivity">
<Button
android:onClick="showLoading"
android:text="默认样式Loading"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<Button
android:onClick="showCustomLoading"
android:text="自定义样式Loading"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<Button
android:onClick="showClickLoading"
android:text="带有点击事件Loading"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<Button
android:onClick="hideLoading"
android:text="隐藏Loading"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<Button
android:onClick="multiLoading"
android:text="多页面显示Loading"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<Button
android:onClick="listLoading"
android:text="列表显示Loading"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<FrameLayout
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="match_parent">
</FrameLayout>
</LinearLayout>
再来看看Java里怎么来用它
你只需要调用LoadingBar.show(parent),这个parent是一个View,也就是你要显示的位置。
比如现在这个例子,xml中那个id为content的FrameLayout,就是要显示的内容,然后在网络请求前需要把Loading覆盖在内容上面,所以需要把这个FrameLayout传给LoadingBar,当然如果你把getWindow().getDecorView()传给了LoadingBar,Loading就会作用于整个屏幕。
package com.dyhdyh.loadingbar.example;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Toast;
import com.dyhdyh.widget.loadingbar.LoadingBar;
public class MainActivity extends AppCompatActivity {
View mParent;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mParent = findViewById(R.id.content);
}
/**
* 显示默认样式Loading
*
* @param v
*/
public void showLoading(View v) {
LoadingBar.show(mParent);
}
/**
* 显示自定义样式Loading
*
* @param v
*/
public void showCustomLoading(View v) {
LoadingBar.show(mParent, View.inflate(this, R.layout.custom_loading, null), null);
}
/**
* 带有点击事件Loading
*
* @param v
*/
public void showClickLoading(View v) {
LoadingBar.show(mParent, View.inflate(this, R.layout.custom_error, null), new View.OnClickListener() {
@Override
public void onClick(View v) {
//在这里处理点击LoadingBar后的要做的操作
Toast.makeText(MainActivity.this, "重新加载", Toast.LENGTH_SHORT).show();
showLoading(v);
}
});
}
/**
* 隐藏单个Loading
*
* @param v
*/
public void hideLoading(View v) {
LoadingBar.cancel(mParent);
}
/**
* 取消所有Loading
*
*/
public void cancelAll() {
LoadingBar.cancelAll();
}
/**
* 多页面显示Loading
*
* @param v
*/
public void multiLoading(View v) {
startActivity(new Intent(this, MutiFragmentActivity.class));
}
/**
* 显示列表的Loading
*
* @param v
*/
public void listLoading(View v) {
startActivity(new Intent(this, ListViewActivity.class));
}
}
注意事项
show方法中传的parent,目前支持FrameLayout|RelativeLayout|DrawerLayout|CoordinatorLayout|CardView,这样才能覆盖在内容上面。
源码解析
LoadingBar的灵感来源于SnackBar,SnackBar的原理其实就是寻找最外层的CoordinatorLayout或者FrameLayout,然后添加到布局上,再通过动画显示出来。
LoadingBar的思路大致也是这样,唯一跟SnackBar的区别就是LoadingBar并不是找最外层的Layout,而是直接添加到show方法传过来的ViewGroup上,这样就可以将Loading显示在任意位置,我们先来看看show方法是怎么把Loading显示出来的。
1.show有个两个重载方法,一个参数的会使用默认样式的LoadingView,三个参数的会使用传过来的LoadingView并且设置点击事件。
public static LoadingBar show(View parent) {
return show(parent, null, null);
}
public static LoadingBar show(View parent, View loadingView, View.OnClickListener onClickListener) {
//如果已经有Loading在显示了
LoadingBar loadingBar;
if (mLoadingBars.containsKey(parent)) {
loadingBar = mLoadingBars.get(parent);
} else {
//如果没有就新建一个
loadingBar = new LoadingBar(findSuitableParent(parent));
mLoadingBars.put(parent, loadingBar);
}
//
loadingBar.mParent.removeView(loadingBar.mView);
if (loadingView == null) {
loadingBar.mView = loadingBar.createDefaultLoadingView();
} else {
loadingBar.mView = loadingView;
}
loadingBar.mParent.addView(loadingBar.mView);
if (onClickListener != null) {
loadingBar.mView.setOnClickListener(onClickListener);
}
loadingBar.showView();
return loadingBar;
}
2.show方法中可以看见有调用findSuitableParent(parent),因为LoadingView是要覆盖在内容上面的,那父节点就必须是一个可覆盖的布局,所以在这里检查了一下是否能够让LoadingView遮住内容。
先检查一下parent是不是可覆盖的ViewGroup,如果是就能直接使用,如果不是,就需要循环寻找可用的ViewGroup。
private static ViewGroup findSuitableParent(View parent) { if (parent == null) { return null; } View suitableParent = parent; do { if (suitableParent instanceof FrameLayout || suitableParent instanceof RelativeLayout || "android.support.v4.widget.DrawerLayout".equals(suitableParent.getClass().getName()) || "android.support.design.widget.CoordinatorLayout".equals(suitableParent.getClass().getName()) || "android.support.v7.widget.CardView".equals(suitableParent.getClass().getName())) { return (ViewGroup) suitableParent; } else { final ViewParent viewParent = suitableParent.getParent(); suitableParent = viewParent instanceof View ? (View) viewParent : null; return (ViewGroup) suitableParent; } } while (suitableParent != null); }
3.show方法中还调用了showView,这里很简单,就是把传过来的LoadingView设为可见就行了。
final void showView() {
mView.setVisibility(View.VISIBLE);
}
4.有显示就会需要隐藏,隐藏的代码也很简单,就是把LoadingView设为不可见。
/**
* 取消所有loading
*/
public static void cancelAll() {
for (Map.Entry<View, LoadingBar> entry : mLoadingBars.entrySet()) {
entry.getValue().cancel();
}
}
/**
* 根据父节点取消单个loading
*
* @param parent show传过来的父节点
*/
public static void cancel(View parent) {
mLoadingBars.get(parent).cancel();
}
/**
* 取消loading
*/
public void cancel() {
if (mView != null) {
hideView();
}
}
怎么样,实现起来是不是很简单,用起来也是一句代码,这种思路可以应用在很多别的场景上,所以不得不说Snackbar真的很强大。
对这个组件有不懂的地方可以进群问我:146262062