Android下的仿iOS搜索 - SearchBar

文/BlackSwift(简书作者)
原文链接:http://www.jianshu.com/p/a27881f8564a
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。

本文主要介绍仿iOS的searchview实现,难点主要有两个

  1. Cancel按钮的动画
  2. 用RxJava,Retrofit,Edittext进行网络请求

关键词: UISerchBar on Android , RxJava Edittext , ObjectAnimator

Android下的仿iOS搜索 - SearchBar_第1张图片
RxSearchView

Cancel按钮的动画

按钮动画初看以为是伸缩动画,实际上使用的是左右slide切换的动画,按钮与左边的输入框在布局上是分开的。自定义了一个ViewSwitcher,它继承于FrameLayout,内部实现了左右切换的ObjectAnimator动画,内部装了一个圆角背景,还有一个蓝色按钮

<com.github.xxxx.ViewSwitcher
    ......
    app:vsReserve="8dp">

  
  <TextView 
          ...
      android:text="@android:string/cancel"/>

  
  <View android:layout_width="match_parent" android:layout_height="match_parent"
      android:background="@drawable/internal_search_shape_bg_right_8"/>

com.github.xxxx.ViewSwitcher>

app:vsReserve表示左右切换动画保留的距离,如果为0的话输入框右边的圆角背景就没了。

用RxJava,Retrofit,Edittext进行网络请求

  • 通过debounce(throttleWithTimeout)可以消除按键抖动,不用每按下一次按钮都立刻进行网络请求。debounce内部可以看成栈,下个元素的进入时间间隔小于倒计时就入栈并丢掉栈底的旧元素并重新计时,否则出栈。
  • debounce默认是开了一个非UI线程调度器,所以后面的操作符默认是在非UI线程调用的,必须在UI事件(比如progressbar)调用前设置observeOn(AndroidSchedulers.mainThread());或者手动设置调度器为主线程,它不会造成阻塞

    详情可以看Github或者stackoverflow

  • 使用switchMap与retry可以中断掉之前的请求,节约网络

各种操作符号的在线动画点这里

代码如下:

RxTextView.textChanges(mSearchbar.getEditTextSearch())
    .subscribeOn(AndroidSchedulers.mainThread())
    //delay 500ms
    //debounce and throttle will use different thread after
    .throttleWithTimeout(300, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread())
    .distinct()
    .filter(new Func1() {
      @UiThread @Override public Boolean call(CharSequence charSequence) {
        //void unnecessary request
        return charSequence.length() != 0;
      }
    })
    .map(new Func1() {
      @UiThread @Override public String call(CharSequence charSequence) {
        //fit network api doc require
        return charSequence + "*";
      }
    })
    .doOnNext(new Action1() {
      @UiThread @Override public void call(CharSequence charSequence) {
        progressBar.setVisibility(View.VISIBLE);
        arrayList.clear();
        adapter.notifyDataSetChanged();
      }
    })
    .observeOn(Schedulers.io())
    .switchMap(new Func1>>() {
      @WorkerThread @Override public Observable> call(String s) {
        return repo.getTags(20, s);
      }
    })
    .retry(new Func2() {
      //fix InterruptedIOException bugs on Retrofit
      // when stop old search
      @WorkerThread @Override public Boolean call(Integer integer, Throwable throwable) {
        return throwable instanceof InterruptedIOException;
      }
    })
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(new Subscriber>() {
      @Override public void onCompleted() {

      }

      @Override public void onError(Throwable e) {
        progressBar.setVisibility(View.INVISIBLE);
        e.printStackTrace();
        Toast.makeText(SearchActivity.this, e.getMessage(), Toast.LENGTH_SHORT).show();
      }

      @Override public void onNext(List tags) {
        progressBar.setVisibility(View.INVISIBLE);

        arrayList.clear();
        arrayList.addAll(tags);
        adapter.notifyDataSetChanged();
      }
    });

项目地址

你可能感兴趣的:(个人文章)