自定义 MyScrollView & 联网加载数据的4种状态的抽取及代码优化 — 金融APP03


不否认努力,继续加油!
学习整理重点、盲区,笔记如下:干干巴巴,麻麻赖赖,一点都不圆润……

day03

内容

1. 自定义圆形进度条ui-RoundProgress

  1. 设置圆形进度条,并在中间设置文本,实时更新数据,并设置进度条动态效果,效果图如下:
    在这里插入图片描述
  2. 自定义进度条详细步骤,以及==自定义属性的使用== 在 : 自定义View_圆形进度条&自定义属性的定义和使用.

2. 自定义 ui-MyScrollView,实现头尾部的下拉、上拉

  1. 创建 MyScrollView.java 继承自 ScrollView ;实现,划到头还可以继续滑,划到尾也可以继续划;

  2. 不需要重写 onLayout();因为不是在 Scroll 布局上下添加布局,而是,在触摸事件时,对其进行一个重新布局;

  3. 当 ScrollView 滑动到边缘(头 或 尾时),或者自身动画结束时,才对其处理,否则按照 ScrollView 的默认操作 super();

  4. 滑动原理:手指滑动时,记下布局处于临界位置的坐标,将坐标记录在 new Rectf 中,并将原布局放置在,拖动后的高度;回弹时,设置动画将布局慢慢的回弹到矩形记录的原始坐标处;

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        if (childView == null || !isFinishAnimation) {
            return super.onTouchEvent(ev);
        }
        int eventY = (int) ev.getY();//获取当前的y轴坐标
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                lastY = eventY;
                break;
            case MotionEvent.ACTION_MOVE:
                int dy = eventY - lastY;//微小的移动量
                if (isNeedMove()) {
                    if (normal.isEmpty()) {
                        //记录了childView的临界状态的左、上、右、下
                        normal.set(childView.getLeft(), childView.getTop(), childView.getRight(), childView.getBottom());
                    }
                    //重新布局
                    childView.layout(childView.getLeft(), childView.getTop() + dy / 2, childView.getRight(), childView.getBottom() + dy / 2);
                }
                lastY = eventY;//重新赋值
                break;
            case MotionEvent.ACTION_UP:
                if (isNeedAnimation()) {
                    //使用平移动画
                    int translateY = childView.getBottom() - normal.bottom;
                    TranslateAnimation translateAnimation = new TranslateAnimation(0, 0, 0, -translateY);
                    translateAnimation.setDuration(200);
                  	translateAnimation.setFillAfter(true);//停留在最终位置上
                    translateAnimation.setAnimationListener(new Animation.AnimationListener() {
                        @Override
                        public void onAnimationStart(Animation animation) {
                            isFinishAnimation = false;
                        }
                        @Override
                        public void onAnimationEnd(Animation animation) {
                            isFinishAnimation = true;
                            childView.clearAnimation();//清除动画
                            //重新布局;避免视图动画的属性没有移过来;
                            childView.layout(normal.left, normal.top, normal.right, normal.bottom);
                            //清除normal的数据
                            normal.setEmpty();
                        }
                        @Override
                        public void onAnimationRepeat(Animation animation) {
                        }
                    });
                    childView.startAnimation(translateAnimation);
                }
                break;
        }
        return super.onTouchEvent(ev);
    }
    
    private boolean isNeedMove() {
        //获取子视图的高度
        int childMeasuredHeight = childView.getMeasuredHeight();
        //获取布局的高度
        int scrollViewMeasuredHeight = this.getMeasuredHeight();
        //dy >= 0
        int dy = childMeasuredHeight - scrollViewMeasuredHeight;
        //获取用户在y轴方向上的偏移量 (上 + 下 -)
        int scrollY = this.getScrollY();
        if (scrollY <= 0 || scrollY >= dy) {
        //按照我们自定义的MyScrollView的方式处理
        return true;
    	}
    	//其他处在临界范围内的,返回false。即表示,仍按照ScrollView的方式处理
        return false;
    }
    
  5. 事件冲突处理;这里当 down 在 ViewPager上时,滑动效果有异常情况;故这里在 onInterceptTouchEvent() 中,让父视图对子视图的事件进行拦截;

3. Fragment 的抽取

  1. 将 用到的 多个 Fragment 中公共部分抽取成单独的基类;

4. 联网加载数据的4种状态的抽取及代码优化

  1. 面临的问题;每一个 Fragment 都对应四种状态,加载中;联网获取数据成功且有数据;联网获取数据失败、联网获取数据成功但数据为空;对应的四种布局;

  2. 如果每个 Fragment 都创建这四种状态,获得请求状态后再将多于页面隐藏,也不是不可以……

  3. 创建抽象类 LoadingPage.java

    //提供4种不同的显示状态及当前的状态
    private static final int PAGE_STATE_LOADING = 1;
    private static final int PAGE_STATE_ERROR = 2;
    private static final int PAGE_STATE_EMPTY = 3;
    private static final int PAGE_STATE_SUCCESS = 4;
    //当前的状态:默认是加载状态
    private int page_state_current = 1;
    //2.提供4种不同的界面,并在第3步中初始化
    private View loadingView;
    ……
    //3.1构造器的初始化方法中根据当前的状态,加载不同的界面显示
     private void init() {
        params = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT);
        if(loadingView == null){
            loadingView = UIUtils.getXmlView(R.layout.page_loading);
     addView(loadingView, params);
     }
    	if (errorView == null) {
            errorView = UIUtils.getXmlView(R.layout.page_error);
     addView(errorView, params);
     }
        if (emptyView == null) {
            emptyView = UIUtils.getXmlView(R.layout.page_empty);
     addView(emptyView, params);
     }
        //界面中显示 View 的操作:需要在主线程中执行
        showSafePage();
    }
    //3.2 showSafePage():保证内部的操作在主线程中执行。
    private void showSafePage() {
    	UIUtils.runOnUiThread(new Runnable() {
            @Override
            public void run() {
        		showPage();
     		}
     	});
    }
    //3.3 在如下方法中确定显示的View
    private void showPage() {
        loadingView.setVisibility(page_state_current == PAGE_STATE_LOADING ? View.VISIBLE : View.GONE);
        errorView.setVisibility(page_state_current == PAGE_STATE_ERROR ? View.VISIBLE : View.GONE);
        emptyView.setVisibility(page_state_current == PAGE_STATE_EMPTY ? View.VISIBLE : View.GONE);
    
        if(successView == null){
            successView = UIUtils.getXmlView(layoutId());
     addView(successView,params);
    	 }
        successView.setVisibility(page_state_current == PAGE_STATE_SUCCESS ? View.VISIBLE : View.GONE);
    }
    //4.LadingPage中要显示哪个View,取决于联网获取数据的情况。鉴于不同页面都有联网的需求,进而将联网操作声明在LoadingPage中。
    //定义如下的方法,联网下载数据,并决定了当前的页面状态(加载、失败、空数据、成功)
    public void show(){
        if(page_state_current != PAGE_STATE_LOADING){
            page_state_current = PAGE_STATE_LOADING;
     	}
      	String url = url();
        if(TextUtils.isEmpty(url)){
            resultState = ResultState.SUCCESS;
            resultState.setContent("");
         	loadingPage();
     	}else{
            client.get(url(),params(),new AsyncHttpResponseHandler(){...}
       	}
    }
    //5.使用到的枚举类:
    public enum ResultState {
        ERROR(2), EMPTY(3), SUCCESS(4);
    	private int state;
    	ResultState(int state) {
       		this.state = state;
    	}
    	private String content;//保存的内部数据
    	public String getContent() {
          	return content;
    	}
    	public void setContent(String content) {
    	     this.content = content;
    	}
    }
    
  4. 在 基类 中使用 LoadingPage;

    1. 此时 基类 的回调方法 onCreateView() 将 LoadingPage 作为返回值,实例化 LoadingPage ,并重写所有的抽象方法。

      loadingPage = new LoadingPage(container.getContext()){
          @Override
          public int layoutId() {
              return getLayoutId();
      	 }
      
      	 @Override
      	 protected void onSuccess(ResultState resultState, View successView) {
      		findViews(view_success);
      	 	initTitle();
      	 	initData(resultState.getContent());
      	 }
      
      	 @Override
      	 protected RequestParams params() {
      	     return getParams();
      	 }
      	@Override
      	protected String url() {
      	     return getUrl();
      	}
      };
      return loadingPage;
      
    2. HomeFragment 等 Fragment 中重写所有 基类 中的抽象方法;

    3. 需要 BaseFragment 的 onActivityCreated() 中提供 LoadingPage 中联网操作的调用:

      public void onActivityCreated(@Nullable Bundle savedInstanceState) {
          super.onActivityCreated(savedInstanceState);
          loadingPage.show();
      }
      

      LoadingPage的执行流程by宋红康(尚硅谷老师)

盲区

  1. 声明:本博客根据尚硅谷项目实战: 硅谷金融.学习整理;
  2. 对于画布绘制圆、矩形、圆弧以及确定文本的坐标有所疏忽,也就是有多回顾和收获;
  3. 对于为什么声明自定义属性的理解有了深入认识:让属性就定义在属性的 xml 中;对于如何定义并使用自定义属性有了认识!
  4. 对于代码的抽取,不熟悉,花费时间太长了;尤其是对于今天的 LoadingPage 的抽取,真是要了我老命,我现在还没整明白……
  5. handler.postDelayed() 失效?难道必须放在主线程?又是一个盲区!handler

彩蛋

  1. 菜单是我一个 BUG 改了一天的成果;

  2. 问题如下:

    handler.postDelayed(new Runnable() {
        @Override
        public void run() {
        //中间的代码不执行!
            }
    	}, 1000);
    
  3. 解决办法就是,拒绝延迟;

    new Runnable() {
        @Override
        public void run() {
            }
    }.run();
    
  4. 那么问题来了,为什么 postDelayed() 里面的
    Runnable() 不执行呢???

  5. 或者说, handler 使用,必须搁在主线程??

  6. 不知不觉有暴露出一个盲区嗷……

其他笔记

金融App

  1. 金融APP01—页面架构.
  2. 金融APP02—主页及工具类创建

商城

Android项目实战—— 商城APP.

新闻

Android项目实战—— 新闻APP.

猜你喜欢

转载自blog.csdn.net/liusaisaiV1/article/details/106268250