基本UI组件的封装库(一)——basicUI

查看在线pdf文档:
http://note.youdao.com/s/EM20Cggm

以下是我的基本UI组件该系列的文章,欢迎大家转载和分享:
基本UI组件的封装库(一)——basicUI
基本UI组件的封装库(二)——basicUI
基本UI组件的封装库(三)——basicUI
基本UI组件的封装库(四)——basicUI

初衷

  • 我们在项目开发的时候,通常会遇到很多不同UI要求,可能每次都要写很多次的布局。
  • BasicUI设计初衷就是希望可以提高我们开发的效率和节省时间。
  • 如果大家觉得有点帮助,希望可以抬抬你的贵手,送我一个星星,谢谢。如果有什么问题,也欢迎你在下方留言或者在BasicUI中提

Gradle依赖

  • Step 1. Add the JitPack repository to your build file
    Add it in your root build.gradle at the end of repositories:
    allprojects {
        repositories {
            ...
            maven { url 'https://jitpack.io' }
        }
    }
  • Step 2. Add the dependency
    dependencies {
             implementation 'com.github.Peakmain:BasicUI:1.1.0-androidx'
    }

使用

一、启动器优化

原理

原理图.png

有向无环图的设计


image.png

使用

  • 1、并发运行,只需要继承Task
public class UtilsTask extends Task {
    @Override
    public void run() {
        //处理数据
    }
}
  • 2、在某个Task之后执行,比如极光需要在获取设备id之后执行
    如:获取设备id的Task
public class DeviceIdTask extends Task {
    private String mDeviceId;

    @Override
    public void run() {
        // 真正自己的代码
        TelephonyManager tManager = (TelephonyManager) mContext.getSystemService(
                Context.TELEPHONY_SERVICE);
        mDeviceId = tManager.getDeviceId();
        App app = (App) mContext;
        app.setDeviceId(mDeviceId);
    }
}

极光的Task需要实现dependsOn方法
如下代码

public class JPushTask extends Task {
    @Override
    public List> dependsOn() {
        List> tasks = new ArrayList<>();
        tasks.add(DeviceIdTask.class);
        return tasks;
    }

    @Override
    public void run() {
        //模拟极光推送
        LogUtils.e("极光推送开始");
        App app = (App) mContext;
        LogUtils.e("极光推送获取id:",app.getDeviceId());
    }
}
  • 3、运行在主线程只需要继承MainTask即可
public class WeexTask extends MainTask {
    @Override
    public void run() {
        InitConfig config = new InitConfig.Builder().build();
        WXSDKEngine.initialize((Application) mContext, config);
    }
}
  • 4、想空闲的时候处理数据,只需要在Application中使用DelayInitDispatcher添加相关Task
    DelayInitDispatcher delayInitDispatcher=new DelayInitDispatcher();
    delayInitDispatcher.addTask(任务即可).start();
  • 5、最后只需要在Application中进行初始化和添加运行就可以了
  TaskDispatcher.init(this);
        TaskDispatcher dispatcher = TaskDispatcher.createInstance();
        dispatcher.addTask(new AMapTask())
                .addTask(new UtilsTask())
                .addTask(new JPushTask())
                .addTask(new DeviceIdTask())
                .addTask(new WeexTask())
                .start();

二、工具类封装

image.png
OkHttp的使用
  • get请求方式
     HttpUtils.with(OkHttpActivity.this)
                            .url("http://i.jandan.net/")
                            .addParams("oxwlxojflwblxbsapi", "jandan.get_pic_comments")
                            .addParams("page", "1")
                            .execture(new EngineCallBack() {
                                @Override
                                public void onError(Exception e) {
                                    LogUtils.e(e.getMessage());
                                }

                                @Override
                                public void onSuccess(String result) {
                                    mTvResult.setText(result);
                                }

                            });
  • post请求方式
                    HttpUtils.with(OkHttpActivity.this)
                            .url("https://www.wanandroid.com/user/login")
                            .addParams("username", "peakmain")
                            .addParams("password", "123456")
                            .post()
                            .execture(new EngineCallBack() {
                                @Override
                                public void onError(Exception e) {
                                    LogUtils.e(e.getMessage());
                                }

                                @Override
                                public void onSuccess(String result) {
                                    mTvResult.setText(result);
                                }
                            });
  • 单线程下载
                    File file = new File(Environment.getExternalStorageDirectory(), "test.apk");
                    if (file.exists()) {
                        file.delete();
                    }
                    HttpUtils.with(OkHttpActivity.this)
                            .url("http://imtt.dd.qq.com/16891/apk/87B3504EE9CE9DC51E9F295976F29724.apk")
                            .downloadSingle()
                            .file(file)
                            .exectureDownload(new DownloadCallback() {
                                @Override
                                public void onFailure(Exception e) {
                                    LogUtils.e(e.getMessage());
                                }

                                @Override
                                public void onSucceed(File file) {
                                    ToastUtils.showShort("file下载完成");
                                    LogUtils.e("文件保存的位置:" + file.getAbsolutePath());
                                    mProgressBar.setVisibility(View.GONE);
                                    mProgressBar.setProgress(0);
                                }

                                @Override
                                public void onProgress(int progress) {
                                    LogUtils.e("单线程下载apk的进度:" + progress);
                                    mProgressBar.setProgress(progress);
                                    mProgressBar.setVisibility(View.VISIBLE);
                                }
                            });
  • 多线程下载
                    file = new File(Environment.getExternalStorageDirectory(), "test.apk");
                    if (file.exists()) {
                        file.delete();
                    }
                    HttpUtils.with(OkHttpActivity.this)
                            .url("http://imtt.dd.qq.com/16891/apk/87B3504EE9CE9DC51E9F295976F29724.apk")
                            .downloadMutil()
                            .file(file)
                            .exectureDownload(new DownloadCallback() {
                                @Override
                                public void onFailure(Exception e) {
                                    LogUtils.e(e.getMessage());
                                }

                                @Override
                                public void onSucceed(File file) {
                                    LogUtils.e(file.getAbsolutePath() + "," + file.getName());
                                    Toast.makeText(OkHttpActivity.this, "下载完成", Toast.LENGTH_LONG).show();
                                    mProgressBar.setVisibility(View.GONE);
                                    mProgressBar.setProgress(0);
                                }

                                @Override
                                public void onProgress(int progress) {
                                    LogUtils.e(progress + "%");
                                    mProgressBar.setVisibility(View.VISIBLE);
                                    mProgressBar.setProgress(progress);
                                }
                            });
  • 切换引擎的方法
    默认是okHttpEngine
  HttpUtils.with(OkHttpActivity.this)
   .exchangeEngine(切换的引擎)

或者在application中直接初始化
HttpUtils.init(初始化的引擎);

2、 gilde图片选择库切换
image.png
  • 简单使用
 ImageLoader.getInstance().displayImage(this, data.get(0).getUrl(), mImageView);
  • 占位图的使用
 ImageLoader.getInstance().displayImage(this, data.get(1).getUrl(), mImageView, R.mipmap.ic_default_portrait);
  • 圆角图片
 ImageLoader.getInstance().displayImageRound(this, data.get(2).getUrl(), mImageView,50 ,0);
  • 指定图片的大小
ImageLoader.getInstance().displayImage(this,data.get(4).getUrl(),mImageView,800,800,0);
  • 切换图片加载库
ImageLoader.getInstance().exchangeImageLoader(切换的库)
3、Sqlitedatabase数据库封装的使用
  • 1、需要实体类实现空的构造方法

-2、获得IDaoSupport实体类

IDaoSupport dao = DaoSupportFactory.getInstance().getDao(Person.class);
  • 3、插入数据
  dao.insert(persons);
  • 4、查询数据
    查询所有数据
 List list = dao.querySupport().queryAll();

查询指定数据:如指定指定名字的范围

dao.querySupport().selection("name like ?").selectionArgs(new String[]{"新%"}).query();
  • 5、删除数据
    需要传入两个参数
    1、条件 2、条件参数
    源码
int delete(String whereClause, String... whereArgs);

如下删除年龄109的数据,返回的是删除数据所在位置的索引

dao.delete("age = ?", new String[]{"109"});
  • 6、更新数据
    需要传入三个参数,1、新数据的实体类,2、条件 3、条件参数
    源码
    int update(T obj, String whereClause, String... whereArgs);

如下更新年龄是108的数据

 dao.update(new Person("peakmain", 18), "age =?", new String[]{"108"});

四、关于Recyclerview的使用

  • 单布局继承于 CommonRecyclerAdapter,其中需要的方法是
public CommonRecyclerAdapter(Context context, List data, int layoutId) {}

1、关于设置文本,第一种我们可以直接holder.setText方法

holder.setText(int viewId,CharSequence text)

第二种就是首先getView然后设置文字

TextView tv=holder.getView(view viewId)
tv.setText("")

2、除了设置文本之外,里面还提供了设置文字的颜色,文字的大小,view的点击事件和长按事件,view是否可见,某一条的点击事件

3、关于设置图片,我提供了一个默认Glide加载图片

  holder.setImageByUrl(R.id.iv_meizhi, new GlideImageLoader(item.getUrl()));

如果大家不想使用Glide或者想用自己的Glide,大家可以新建一个类去继承ViewHolder.HolderImageLoade即可

for example:

public class PagePoupAdapter extends CommonRecyclerAdapter{
    private int mSelectPosition=0;
    public PagePoupAdapter(Context context, List data) {
        super(context, data, R.layout.item_popup_window);
    }

    @Override
    public void convert(ViewHolder holder, String item) {
        TextView tvName=holder.getView(R.id.tv_name);
        tvName.setText(item);
       tvName.setTextColor(mContext.getResources().getColor(R.color.colorAccent));
    }
}
  • 关于多布局
    也是继承extends CommonRecyclerAdapter.但是实现的方法是
 public CommonRecyclerAdapter(Context context, List data, MultiTypeSupport multiTypeSupport) {}

for example:

    public SearchCityAdapter(Context context, List data) {
        super(context, data, new MultiTypeSupport() {
            @Override
            public int getLayoutId(SearchCityBean item, int position) {
                return R.layout.default_city_recycler_item;
            }
        });
    }
    @Override
    public void convert(ViewHolder holder, SearchCityBean item) {
        int itemViewType = getItemViewType(holder.getAdapterPosition());
        if (itemViewType == R.layout.default_city_recycler_item) {
            holder.setText(R.id.tv_city_name, item.getName());
        }
    }
  • 我们有时候会使用到recycleview的悬浮列表,这里我也提供了一个基本的悬浮列表BaseSuspenisonItemDecoration,使用很简单,大家只需要继承BaseSuspenisonItemDecoration即可
    因为有时候我们需要自定背景颜色和文字颜色,文字的大小,悬浮之间的距离等。这里我提供了Builder方法,大家继承即可

for example:

public class SectionItemDecoration extends BaseItemDecoration {

    public SectionItemDecoration(Builder builder) {
        super(builder);
    }
    @Override
    public String getTopText(List data, int position) {
        return data.get(position).getSection();
    }
    public static class Builder extends BaseItemDecoration.Builder{

        public Builder(Context context, List data) {
            super(context, data);
        }
        @Override
        public SectionItemDecoration create() {
            return new SectionItemDecoration(this);
        }
    }
}

使用非常简单

   SectionItemDecoration decoration = new SectionItemDecoration.Builder(this, mAllCities)
                .setBgColor(ContextCompat.getColor(this,R.color.colorAccent))
                .setTextColor(ContextCompat.getColor(this,R.color.color_black))
                .setSectionHeight(SizeUtils.dp2px(this,30))
                .create();

recyclerview实现下拉刷新、加载更多和多状态布局

  • 封装库类中我封装了一个默认的下拉刷新和加载更多
  • 1、布局
    
  • 2、下拉刷新
        mRecyclerView.addRefreshViewCreator(new DefaultRefreshViewCreator());
        mRecyclerView.setOnRefreshListener(this);
  • 3、重写onRefresh的方法,数据获取完之后在其中调用onStopRefresh方法停止下拉刷新
    比如示例中的代码
    public void onRefresh() {
        new Handler().postDelayed(() -> {
            List data = getData();
            mAdapter.setData(data);
            mRecyclerView.onStopRefresh();
        }, 2000);
    }
  • 4、加载更多
  mRecyclerView.addLoadViewCreator(new DefalutLoadViewCreator());
        mRecyclerView.setOnLoadMoreListener(this);
  • 5、重写onLoad方法,数据获取完之后在其中调用onStopLoad方法停止加载更多
    比如示例中的代码
   public void onLoad() {
        new Handler().postDelayed(() -> {
            List moreData = getMoreData();
            mAdapter.addData(moreData);
            mRecyclerView.onStopLoad();
        }, 2000);

    }
  • 6、大家还可以自定以下拉刷新和加载更多
    下拉刷新继承RefreshViewCreator实现相关功能即可
    上图下拉刷新效果2示例代码
public class BestMissRefreshCreator extends RefreshViewCreator {
    // 加载数据的ImageView
    private ImageView mRefreshIv;
    @Override
    public View getRefreshView(Context context, ViewGroup parent) {
        View refreshView = LayoutInflater.from(context).inflate(R.layout.layout__bestmiss_refresh_header_view, parent, false);
        mRefreshIv = refreshView.findViewById(R.id.img_progress);
        return refreshView;
    }

    @Override
    public void onPull(int currentDragHeight, int refreshViewHeight, int currentRefreshStatus) {
        if (currentRefreshStatus == LoadRefreshRecyclerView.LOAD_STATUS_PULL_DOWN_REFRESH) {
            mRefreshIv.setImageResource(R.drawable.list_view_pull);
        }
        if (currentRefreshStatus == LoadRefreshRecyclerView.LOAD_STATUS_LOOSEN_LOADING) {
            mRefreshIv.setImageResource(R.drawable.list_view_release);
        }
    }

    @Override
    public void onRefreshing() {
        mRefreshIv.setImageResource(R.drawable.load_more_anim);
        ((AnimationDrawable) mRefreshIv.getBackground()).start();
    }
    @Override
    public void onStopRefresh() {
        // 停止加载的时候清除动画
        mRefreshIv.setRotation(0);
        ((AnimationDrawable) mRefreshIv.getBackground()).stop();
        mRefreshIv.clearAnimation();
    }
}

加载更多继承LoadViewCreator即可
上图下拉刷新2示例代码

public class LoadMoreCreator extends LoadViewCreator {
    // 加载数据的ImageView
    private TextView mLoadTv;
    private View mRefreshIv;

    @Override
    public View getLoadView(Context context, ViewGroup parent) {
        View refreshView = LayoutInflater.from(context).inflate(R.layout.layout_load_footer_view, parent, false);
        mLoadTv = (TextView) refreshView.findViewById(R.id.load_tv);
        mRefreshIv = refreshView.findViewById(R.id.refresh_iv);
        return refreshView;
    }

    @Override
    public void onPull(int currentDragHeight, int refreshViewHeight, int currentRefreshStatus) {
        if (currentRefreshStatus == LoadRefreshRecyclerView.LOAD_STATUS_PULL_DOWN_REFRESH) {
            mLoadTv.setText("上拉加载更多");
        }
        if (currentRefreshStatus == LoadRefreshRecyclerView.LOAD_STATUS_LOOSEN_LOADING) {
            mLoadTv.setText("松开加载更多");
        }
    }

    @Override
    public void onLoading() {
        mLoadTv.setVisibility(View.INVISIBLE);
        mRefreshIv.setVisibility(View.VISIBLE);

        // 加载的时候不断旋转
        RotateAnimation animation = new RotateAnimation(0, 720,
                Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
        animation.setRepeatCount(-1);
        animation.setDuration(1000);
        mRefreshIv.startAnimation(animation);
    }

    @Override
    public void onStopLoad() {
        // 停止加载的时候清除动画
        mRefreshIv.setRotation(0);
        mRefreshIv.clearAnimation();
        mLoadTv.setText("上拉加载更多");
        mLoadTv.setVisibility(View.VISIBLE);
        mRefreshIv.setVisibility(View.INVISIBLE);
    }


    @Override
    public void onFinishLoadData() {
       mLoadTv.setText("无更多数据");
    }
}

RecycleView实现多状态布局

多状态布局.gif

使用
使用WarpRecyclerView(只能添加头部或尾部)、RefreshRecyclerView(下拉刷新)或者LoadRefreshRecyclerView(加载更多或者下拉刷新)

  • 1、可以自定义状态布局,一共四个属性
    
        
        
        
        
    

所以使用示例如下

    

注意:这里的emptyView、errorView、noNetworkView布局的根布局需要设置id,对应的id分别是:empty_retry_view、error_retry_view、no_network_retry_view

  • 2、basicUI提供了默认的状态布局,可不设置自定义属性

  • 3、没有网络显示

 mRecyclerView.showNoNetwork();
  • 4、空布局
    这里大家放在什么位置都可以,源码中会自动根据是否有数据进行显示或隐藏
mRecyclerView.showEmptyView();
  • 5、显示loading和隐藏loading
  mRecyclerView.showLoading();
mRecyclerView.hideLoading();
  • 6、显示错误
 mRecyclerView.showError();
  • 7、显示内容
mRecyclerView.showContentView();
  • 8、设置emptyView、errorView、noNetworkView可以设置点击事件
 mRecyclerView.setOnRetryClickListener(v -> ToastUtils.showShort("正在重新请求接口..."));

五、关于NavigationBar的使用

我们每次在项目添加头部的时候,一般做法都是说定义一个公用的布局,但是这其实并不友好,而且都需要findVIewById,我这里用了Builder设计模式,可以动态添加头部

    mDefaultNavigationBar = new DefaultNavigationBar
                  .Builder(this, (ViewGroup) findViewById(android.R.id.content))
                  //Whether to display the return button
                  .setDisplayHomeAsUpEnabled(true)
                  //Set left click event
                  .setLeftClickListener(v -> {
  
                  })
                  //Whether to display the title that comes with the toolbar by default
                  .setDisplayShowTitleEnabled(true)
                  //Hide right view
                  .hideRightView()
                  //Set the click event of the return button
                  .setNavigationOnClickListener(v -> finish())
                  //set left text color
                  .setLeftTextColor(ContextCompat.getColor(this,R.color.color_272A3D))
                  //set title
                  .setTitleText("")
                  //set toolbar background color
                  .setToolbarBackgroundColor(0)
                  .create();
image.png

六、关于AlertDialog

支持从底部弹出,支持宽度全屏,支持设置动画

        AlertDialog dialog = new AlertDialog.Builder(ImagePreviewActivity.this)
                .setContentView(R.layout.dialog_show_image_deal)
                .fromButtom(true)
                // Set click events for view
                .setOnClickListener(R.id.bt_logout, new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                    }
                })
                //set animation
                .setAnimation(R.style.dialog_from_bottom_anim)
                //Eject from bottom
                .fromButtom(true)
                //set width  MATCH_PARENT
                .setFullWidth()
                .show();
image.png

七、关于PopupWindow的封装

       new CustomPopupWindow.PopupWindowBuilder(this)
                .setView(R.layout.popup_window_view)
                .enableBackgroundDark(true)
                .setAnimationStyle(R.style.PopupWindowAnimation)
                .setBgDarkAlpha(0.7f)
                .create();

八、TextView的封装

这个支持设置背景颜色,背景的圆角,线条的颜色,线条的宽度,支持文字上下左右图片资源两者居中,减少布局嵌套,使用方法还是android:drawableLeft=""

    
image.png

说明:一共有四个属性:shapeTvStrokeWidth线的宽度、shapeTvStrokeColor线的颜色、shapeTvRadius圆角的半径、shapeTvBackgroundColor背景颜色

九、EditText的封装

自带清除按钮的图标,并且可对删除按钮进行填色,或者对删除按钮图标进行替换

    

说明:一共有11个属性:字体大小adet_text_size、字体颜色adet_text_color、未输入文字时字体颜色adet_hint_color、内容是否居上adet_isTop、内容内边距离顶部的距离adet_padding_top、hint的资源adet_hint、是否单行adet_isSingle、输入类型android:inputType、输入长度adet_max_length、删除的图片的颜色adet_tint_color、删除图片的资源adet_delete_src

结语

如果大家感兴趣想知道更多的使用,大家可以看我实战项目wanandorid

我的项目BasicUI的Github地址:https://github.com/Peakmain/BasicUI

你可能感兴趣的:(基本UI组件的封装库(一)——basicUI)