Android Wear(Wear OS)开发

——简化Wear版Wx开发过程中的一些总结

1. 微光模式

智能手表的电池容量都比较低,导致续航也比较短。为了延长手表的续航,Wear OS 手表在没有操作一段时间后,会进入微光模式 AmbientMode。微光模式就是一个省电模式,这个模式会在低功耗下运行,默认情况下,手表会离开当前的应用,返回到表盘的界面。但有时候,我们希望开发的应用在某些情况下,可以一直保持可见的状态,这就需要我们使用支持微光模式的 Activity 了。

  • 一般wear应用的activity继承自WearableActivity,然后activity里调用setAmbientEnabled()来支持微光模式,但是我们的工程的页面架构是一个activity+多fragment的形式;这要求我们的activity必须继承自FragmentActivity,因而需要使用另一种方式来支持微光模式: HomeActivity继承FragmentActivity,同时实现AmbientModeSupport.AmbientCallbackProvider接口,在onCreate里调用AmbientModeSupport.attach(this),它返回一个AmbientModeSupport.AmbientController对象,可用于查询当前微光模式所处的状态。
  public class HomeActivity extends FragmentActivity implements AmbientModeSupport.AmbientCallbackProvider
复制代码
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_home);
        Log.d(TAG, "onCreate");
        AmbientModeSupport.attach(this);
    }
复制代码

AmbientModeSupport.AmbientCallbackProvider定义如下,我们可以实现这些接口,来对微光模式的不同状态进行处理。

public abstract static class AmbientCallback {
    public AmbientCallback() {
    }

    public void onEnterAmbient(Bundle ambientDetails) {
    }

    public void onUpdateAmbient() {
    }

    public void onExitAmbient() {
    }

    public void onAmbientOffloadInvalidated() {
    }
}
复制代码

2. Wear OS右划退出

  • wear针对穿戴设备的操作特点,有个默认的手势:右划退出当前全屏Activity,这对于一般页面操作很方便,ios和Android应用很多都有类似的支持,但对于手表这种屏幕比较小设备,wear提供的是全屏响应手势的,并不像手机是左侧边缘才响应,这样wear的交互设计上一般不建议再使用水平滑动的手势,但凡事总有例外,我们的应用中就有一个右划的手势操作,解决方案就是使用SwipeDismissFrameLayout来包裹我们的视图,我们的视图需要重写canScrollHorizontally方法,返回true,这时就启动了边缘滑动状态(屏幕左侧10%的位置才相应水平滑动手势)。
  • 布局文件
<android.support.wear.widget.SwipeDismissFrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/tools"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">

    <com.tencent.ui.voip.SwipeViewContainer
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        
        ...
    com.tencent.ui.voip.SwipeViewContainer>
android.support.wear.widget.SwipeDismissFrameLayout>
复制代码
  • 自定义View SwipeViewContainer,canScrollHorizontally返回true。
public class SwipeViewContainer extends RelativeLayout {
    ....

    @Override
    public boolean canScrollHorizontally (int direction) {
        return true;
    }
}
复制代码
  • 右划退出在实际使用过程中发现一个问题,被划出的页面(Fragment)退出后又会再闪现了一下,造成很不好的体验,在网上没有找到有用的信息,只能自己摸索。
    • 首先想到的是不是fragment的切换动画引起的, FragmentTransaction定义了如下几种切换方式,但即使设置成TRANSIT_NONE,还是依然会有问题,看来不是切换动画的问题。
    public static final int TRANSIT_ENTER_MASK = 4096;
    public static final int TRANSIT_EXIT_MASK = 8192;
    public static final int TRANSIT_UNSET = -1;
    public static final int TRANSIT_NONE = 0;
    public static final int TRANSIT_FRAGMENT_OPEN = 4097;
    public static final int TRANSIT_FRAGMENT_CLOSE = 8194;
    public static final int TRANSIT_FRAGMENT_FADE = 4099;
    复制代码
    • 然后想能不能在fragment切换过程中对旧页面进行一些操作,这就需要对滑动操作的过程进行监听,SwipeDismissFrameLayout类提供了实现的方法,让我们的视图包裹在SwipeDismissFrameLayout里,然后设置回调SwipeDismissFrameLayout.Callback,在事件的回调方法里进行相关处理。
    private final SwipeDismissFrameLayout.Callback mCallback =
        new SwipeDismissFrameLayout.Callback() {
            public void onDismissed(SwipeDismissFrameLayout layout) {
                Log.d(TAG, "onDismissed()");
                //To do
            }
            public void onSwipeStarted(SwipeDismissFrameLayout layout) {
                Log.d(TAG, "onSwipeStarted()");
            }
            public void onSwipeCanceled(SwipeDismissFrameLayout layout) {
                Log.d(TAG, "onSwipeCanceled()");
            }
        };
    public View onCreateView(@NonNull LayoutInflater inflater, @LayoutRes int layoutid) {
        SwipeDismissFrameLayout swipeLayout = new SwipeDismissFrameLayout(getActivity());
        inflatedView = inflater.inflate(layoutid, swipeLayout, false);
    
        swipeLayout.addView(inflatedView);
        swipeLayout.addCallback(mCallback);
        return swipeLayout;
    }
    复制代码
    一开始想在onDismissed回调里将当期fragment隐藏(如下),但仍没有任何用处,即使在onSwipeStarted调用没任何作用,最后直接来最暴力的,将当期fragment的View直接设置成GONE,这样闪现的问题就解决了。
    //不行
    getActivity().getSupportFragmentManager().beginTransaction().hide(curFragment);
    //搞定
    inflatedView.setVisibility(View.GONE);
    复制代码

3.Fragment

  • 应用中多个frament彼此进行切换,要求右划后可以回到上一个fragment,要使用add而不是replace,这里需要操作fragment的回退栈。

进入

FragmentTransaction transaction =  getActivity().getSupportFragmentManager().beginTransaction();
transaction.setCustomAnimations(R.anim.slide_right_in, R.anim.slide_right_out);
transaction.add(R.id.fragment_container, messageListFragment);
transaction.addToBackStack(null);
transaction.commit();
复制代码

回退

getActivity().getSupportFragmentManager().popBackStack();
复制代码

同一个fragment不能重复add,不然会出错,这里需要处理一下

if (messageFragment.isAdded()){
    transaction.remove(messageFragment);
}
复制代码
  • 使用回退的方式进入原来的fragment,是不会调用Fragment的任何生命周期回调的,那我们有时候原页面又需要知道这个操作,比如需要更新一下页面的数据或状态等,那我们就需要手动去监听,那监听源是什么呢,我们想到这里发生改变的全局数据就是我们的回退栈,而FragmentManager刚好也提供监听回退栈变化的listener,我们重写listener里的onBackStackChanged接口,在里面我们找到要进入的fragment,然后主动触发它的生命周期方法,比如onResume。
  FragmentManager mgrFragment = getSupportFragmentManager();
  mgrFragment.addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
      @Override
      public void onBackStackChanged() {
          FragmentManager manager = getSupportFragmentManager();
          if (fragmentStackSize > manager.getBackStackEntryCount()) {//判断是回退操作
              if (manager != null) {
                  Fragment currFrag = manager.findFragmentById(R.id.fragment_container);
                  currFrag.onResume();
              }
          }
          fragmentStackSize = manager.getBackStackEntryCount();
      }
  });
复制代码

4.性能问题

应用里有用RecycleView来显示列表,列表里有图片,我们使用Glide来显示的,这里需要注意的一点是,在使用Glide的时候,应该在别处对其进行一下初始化,不要在onBindViewHolder里就直接使用,这会导致第一次打开页面很慢。同样道理,onBindViewHolder里用到东西,最好都事先加载好。图片可以根据实际需求进行剪裁,避免使用原图。

RequestOptions options = new RequestOptions();
        options.centerCrop();
        options.override(width, height);
        Glide.with(mContext).load(conversation.getHeadimagepath()).into(holder.avatar);
复制代码

5.总结

以上就是这次这个小项目开发过程中的一些点,并没有高大上的东西,自己做个记录,如果能帮助到你那就更好了

你可能感兴趣的:(Android Wear(Wear OS)开发)