关于手势冲突问题

  • TextView的滑动和RecyclerView冲突
  1. 问题描述:RecycleView中使用了TextView,而TextView大小有限,要求文本支持滚动,此时会与Recycler的滑动冲突;
  2. 问题解决:监听TextView文本的滚动事件,如果是TextView需要滚动时,禁止父控件(RecyclerView)的滑动,否则允许父控件滑动;
tvContent.setMovementMethod(ScrollingMovementMethod.getInstance());
tvContent.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                v.getParent().requestDisallowInterceptTouchEvent(true);
                switch (event.getAction()) {
                    case MotionEvent.ACTION_UP:
                        v.getParent().requestDisallowInterceptTouchEvent(false);
                        break;
                }
                return false;
            }
        });
  • Scrollview的滑动和RecyclerView冲突
  1. 问题描述:一个比较长的界面一般都是Scrollview嵌套RecyclerView来解决.不过这样的UI并不是我们开发人员想看到的,实际上嵌套之后.因为Scrollview和RecyclerView都是滑动控件.会有一点滑动上的冲突.导致滑动起来有些卡顿.
  2. 问题解决:禁止RecycleView的滑动,我们重写一下LayoutManager就行了;
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false) {
            @Override
            public boolean canScrollVertically() {
                return false;
            }
        };
recyclerview.setLayoutManager(linearLayoutManager);
recyclerview.setAdapter(tempCommonAdapter);
  •  ViewPager和 SwipeRefreshLayout冲突
  1. 问题描述: 当 SwipeRefreshLayout 中有 ViewPager 控件, 两者的滑动会相互冲突. 具体表现为 ViewPager 的左右滑动不顺畅, 容易被 SwipeRefreshLayout 拦截(即出现刷新的 View ).
  2. 问题解决: ViewPager 本身是处理了滚动事件的冲突, 它在横向滑动时会调用 requestDisallowInterceptTouchEvent() 方法使父控件不拦截当前的 Touch 事件序列. 但是 SwipeRefreshLayout 的 requestDisallowInterceptTouchEvent() 方法置空了, 所以仍然会拦截当前的 Touch 事件序列.
1. //非法按键
2. private static final int INVALID_POINTER = -1;
3.
4. //dispatch方法记录第一次按下的x
5. private float mInitialDisPatchDownX;
6.
7. //dispatch方法记录第一次按下的y
8. private float mInitialDisPatchDownY;
9.
10. //dispatch方法记录的手指
11. private int mActiveDispatchPointerId = INVALID_POINTER;
12.
13. //是否请求拦截
14. private boolean hasRequestDisallowIntercept = false;
15.
16. @Override
17. public void requestDisallowInterceptTouchEvent(boolean b) {
18.     hasRequestDisallowIntercept = b;
19.     // Nope.
20. }
21.
22. @Override
23. public boolean dispatchTouchEvent(MotionEvent ev) {
24.     switch (ev.getAction()) {
25.         case MotionEvent.ACTION_DOWN:
26.             mActiveDispatchPointerId = MotionEventCompat.getPointerId(ev, 0);
27.             final float initialDownX = getMotionEventX(ev, mActiveDispatchPointerId);
28.             if (initialDownX != INVALID_POINTER) {
29.                 mInitialDisPatchDownX = initialDownX;
30.             }
31.             final float initialDownY = getMotionEventY(ev, mActiveDispatchPointerId);
32.             if (mInitialDisPatchDownY != INVALID_POINTER) {
33.                 mInitialDisPatchDownY = initialDownY;
34.             }
35.             break;
36.         case MotionEvent.ACTION_MOVE:
37.             if (hasRequestDisallowIntercept) {
38.                 //解决viewPager滑动冲突问题
39.                 final float x = getMotionEventX(ev, mActiveDispatchPointerId);
40.                 final float y = getMotionEventY(ev, mActiveDispatchPointerId);
41.                 if (mInitialDisPatchDownX != INVALID_POINTER && x != INVALID_POINTER &&
42.                         mInitialDisPatchDownY != INVALID_POINTER && y != INVALID_POINTER) {
43.                     final float xDiff = Math.abs(x – mInitialDisPatchDownX);
44.                     final float yDiff = Math.abs(y – mInitialDisPatchDownY);
45.                     if (xDiff > mTouchSlop && xDiff * 0.7f > yDiff) {
46.                         //横向滚动不需要拦截
47.                         super.requestDisallowInterceptTouchEvent(true);
48.                     } else {
49.                         super.requestDisallowInterceptTouchEvent(false);
50.                     }
51.                 } else {
52.                     super.requestDisallowInterceptTouchEvent(false);
53.                 }
54.             }
55.             break;
56.         case MotionEvent.ACTION_UP:
57.         case MotionEvent.ACTION_CANCEL:
58.             if (ev.getAction() == MotionEvent.ACTION_UP || ev.getAction() == MotionEvent.ACTION_CANCEL) {
59.                 hasRequestDisallowIntercept = false;
60.             }
61.             break;
62.     }
63.
64.     return super.dispatchTouchEvent(ev);
65. }
66.
67. private float getMotionEventY(MotionEvent ev, int activePointerId) {
68.     final int index = MotionEventCompat.findPointerIndex(ev, activePointerId);
69.     if (index < 0) {
70.         return -1;
71.     }
72.     return MotionEventCompat.getY(ev, index);
73. }
74.
75. private float getMotionEventX(MotionEvent ev, int activePointerId) {
76.     final int index = MotionEventCompat.findPointerIndex(ev, activePointerId);
77.     if (index < 0) {
78.         return -1;
79.     }
80.     return MotionEventCompat.getX(ev, index);
81. }
  • RecyclerView嵌套ViewPager的冲突
  1. 问题描述: 当用户滑动 RecyclerView 后放开手指, RecyclerView 会继续滑动并处于 Fling 状态. 此时用户重新触摸屏幕, RecyclerView 滑动停止, 但是无法左右滑动 ViewPager , 只能上下滑动 RecyclerView .  当用户重新触摸屏幕, 此时 RecyclerView 的 onInterceptTouchEvent() 方法还是返回了 true , 所以 ViewGroup 还是继续拦截了事件, 导致 ViewPager 无法处理.
  2. 问题解决: 如果是 Fling 状态的 RecyclerView , 在处理 ACTION_DOWN 事件时, 应该与 IDLE 状态下保持一致. Fling 状态下处理 ACTION_DOWN , onInterceptTouchEvent() 方法应该返回 false.
1. @Override
2. public boolean onInterceptTouchEvent(MotionEvent e) {
3.     //isScrolling 为 true 表示是 Fling 状态
4.     boolean isScrolling = getScrollState() == SCROLL_STATE_SETTLING;
5.     boolean ans = super.onInterceptTouchEvent(e);
6.     if (ans && isScrolling && e.getAction() == MotionEvent.ACTION_DOWN) {
7.         //先调用 onTouchEvent() 使 RecyclerView 停下来
8.         onTouchEvent(e);
9.         //反射恢复 ScrollState
10.         try {
11.             Field field = RecyclerView.class.getDeclaredField(“mScrollState”);
12.             field.setAccessible(true);
13.             field.setInt(this, SCROLL_STATE_IDLE);
14.         } catch (NoSuchFieldException e1) {
15.             e1.printStackTrace();
16.         } catch (IllegalAccessException e1) {
17.             e1.printStackTrace();
18.         }
19.         return false;
20.     }
21.     return ans;
22. }

关于地图

  • 给一个经纬度在第三方地图打开并显示
public static String GAODE_MAP = “com.autonavi.minimap”;//高德
public static String BAIDU_MAP = “com.baidu.BaiduMap”;//百度
public static String TENXUN_MAP = “com.tencent.map”;//腾讯
/**
* 判断当前设备是否安装某个应用
* @param context
* @param packageName
* @return
*/
public static boolean isInstalled(Context context, String packageName) {
    boolean installed = false;
    if (TextUtils.isEmpty(packageName)) {
        return false;
    }
    List<ApplicationInfo> installedApplications = context.getPackageManager().getInstalledApplications(0);
    for (ApplicationInfo in : installedApplications) {
        if (packageName.equals(in.packageName)) {
            installed = true;
            break;
        } else {
            installed = false;
        }
    }
    return installed;
}
/**
* 打开地图
*/
private void openMap() {
    if (OperateUtil.isInstalled(mActivity, AppConfig.BAIDU_MAP)
            || OperateUtil.isInstalled(mActivity, AppConfig.GAODE_MAP)
            || OperateUtil.isInstalled(mActivity, AppConfig.TENXUN_MAP)) {
        Uri uri = Uri.parse(String.format(“%1$s%2$s,%3$s”, “geo:”, latitude, longitude));
        Intent intent = new Intent(Intent.ACTION_VIEW, uri);
        startActivity(intent);
    } else {
        showToast(“请安装第三方地图!”);
    }
}
  • 给两个定位坐标,通过第三方地图从一个坐标到另外一个坐标导航
private double longitude;//精度
private double latitude;//纬度
private String thisAddressName;//当前地址名称
private double tolongitude;//精度
private double tolatitude;//纬度
private String addressName;//目的地地址名称
private LocationClient mLocationClient = null;
private BDLocationListener myListener = new MyLocationListener();
public static String GAODE_MAP = “com.autonavi.minimap”;//高德
public static String BAIDU_MAP = “com.baidu.BaiduMap”;//百度
public static String TENXUN_MAP = “com.tencent.map”;//腾讯
//打开地图并导航
private void goNavigation(int type) {
    Intent intent = null;
    switch (type) {
        case 0:
            intent = new Intent();
            intent.setData(Uri.parse(String.format(“baidumap://map/navi?location=%1$s,%2$s&src=andr.baidu.openAPIdemo”, tolongitude, tolatitude)));
            break;
        case 1:
            intent = new Intent();
            intent.setData(Uri.parse(String.format(“androidamap://navi?sourceApplication=appname&poiname=fangheng&lat=%1$s&lon=%2$s&dev=1&style=2”, tolongitude, tolatitude)));
            break;
        case 2:
            if (longitude == 0 || latitude == 0) {
                ToastView.showToast(mActivity, “正在定位中,请稍后…”);
                getThisLL();
            } else {
                intent = new Intent();
                intent.setData(Uri.parse(String.format(“qqmap://map/routeplan?type=drive&from=%1$s&fromcoord=%2$s,%3$s&to=%4$s&tocoord=%5$s,%6$s&referer=OB4BZ-D4W3U-B7VVO-4PJWW-6TKDJ-WPB77”, thisAddressName, latitude, longitude, addressName, tolongitude, tolatitude)));
            }
            break;
    }
    if (intent != null) {
        mActivity.startActivity(intent);
    }
}
//开启定位
public void getThisLL() {
    mLocationClient = new LocationClient(mActivity.getApplicationContext());
    //声明LocationClient类
    mLocationClient.registerLocationListener(myListener);
    BaiduMapHelpUtil.initLocation(mLocationClient);
}
//定位导航
public class MyLocationListener implements BDLocationListener {
    @Override
    public void onReceiveLocation(BDLocation location) {
        longitude = location.getLongitude();
        latitude = location.getLatitude();
        thisAddressName = location.getAddress().street;
        mLocationClient.stop();
        Intent intent = new Intent();
        intent.setData(Uri.parse(String.format(“qqmap://map/routeplan?type=drive&from=%1$s&fromcoord=%2$s,%3$s&to=%4$s&tocoord=%5$s,%6$s&referer=OB4BZ-D4W3U-B7VVO-4PJWW-6TKDJ-WPB77”, thisAddressName, latitude, longitude, addressName, tolongitude, tolatitude)));
        mActivity.startActivity(intent);
    }
}

RxJava基本变换使用

一、基本案例实现
public void click(View v){
        // 想象工厂中的流水线
        // 创建 Observable 被观察者 / 可观察对象
        Observable.create(new Observable.OnSubscribe<String>() {
            @Override
            public void call(Subscriber<? super String> subscriber) {
                int result = 10 / 5;
                // onNext 可以调用多次
                subscriber.onNext(result + “”);
                subscriber.onNext(“itcast”);
                // 事件序列结束标记
                subscriber.onCompleted();
                // 事件错误的标记
                //subscriber.onError();
            }
        }).subscribe(new Observer<String>() {
          // 订阅事件 指定观察者, 被观察者必须指定了观察者, 整个事件流程才可以被启动
            @Override
            public void onNext(String s) {
                System.out.println(“onNext: ” + s);
            }
            @Override
            public void onError(Throwable e) {
            }
            @Override
            public void onCompleted() {
                System.out.println(“onCompleted”);
            }
        });
    }

二、被观察者的变形
public void click1(View v){
    Observable.just(1, 2)
            .subscribe(new Observer<Integer>() { // 观察者
                @Override
                public void onCompleted() {
                    System.out.println(“onCompleted”);
                }
                @Override
                public void onError(Throwable e) {
                }
 
                @Override
                public void onNext(Integer integer) {
                    System.out.println(“onNext: ” + integer);
                }
            });
}

三、变换案例1
public void click2(View v) {
    String[] array = {“url1”, “url2”};
    // 订阅者
    subscriber = new Subscriber<String>() {
        @Override
        public void onCompleted() {
            System.out.println(“onCompleted”);
        }
 
        @Override
        public void onError(Throwable e) {
        }
 
        @Override
        public void onNext(String s) {
            System.out.println(“onNext: ” + s);
        }
    };
    Observable.from(array)
            .subscribe(subscriber);
}

三、变换案例2
public void click3(View v) {
    String[] array = {“url1”, “url2”, “url3”};
 
    Observable.from(array)
            .subscribe(new Action1<String>() { // onNext闭包
                @Override
                public void call(String s) {
                    System.out.println(“next call: ” + s);
                }
            });
}

三、变换案例3
public void click4(View v) {
    String[] array = {“url1”, “url2”, “url3”};
 
    Observable.from(array)
            .subscribe(new Action1<String>() { // onNext闭包
                @Override
                public void call(String s) {
                    System.out.println(“next call: ” + s);
                }
            }, new Action1<Throwable>() { // onError
                @Override
                public void call(Throwable throwable) {
 
                }
            }, new Action0() {
                @Override
                public void call() {  // Complete
                    System.out.println(“complete: “);
                }
            });
}

DialogFragment的使用

  • Activity向Dialog传递数据
 
public static MyDialogFragment newInstance(int num) {
        MyDialogFragment fragment = new MyDialogFragment();
        // Supply num input as an argument.
        Bundle args = new Bundle();
        args.putInt(“num”, num);
        fragment.setArguments(args);
        return fragment;
    }
 
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    mNum = getArguments().getInt(“num”);
}
  • Dialog向Activity传参
//利用接口传参,在DialogFragment中定义接口
public interface DialogListener {
  void onComplete(String result);
}
 
//Activity实现该接口
public class MyActivity extends Activity implements DialogListener {
// …
   @Override
   public void onComplete(String result){
     //使用参数
   }
}
 
//在DialogFragment中传参数
DialogListener listener=(DialogListener)getActivity();
listener.onComplete(result); //传参数
  • 设置属性:宽高、背景、位置等
   在xml中设置和onCreateView(), onViewCreated()中设置无效. 在onStart()和onResume()中设置才有效.    
    @Override
    public void onStart() {
        super.onStart();
        Dialog dialog = getDialog();
        if (dialog != null) {
            getDialog().getWindow().setBackgroundDrawableResource(android.R.color.transparent);
            dialog.getWindow().setLayout(AutoUtils.getPercentWidthSize(1024), AutoUtils.getPercentHeightSize(684));
            dialog.getWindow().setGravity(Gravity.CENTER);
        }
    }
 
  • 设置背景Activity的明暗度
// 0-1 , 1表示完全昏暗
getDialog().getWindow().setDimAmount(0.8f);
  • 去除标题栏
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
     getDialog().requestWindowFeature(Window.FEATURE_NO_TITLE); //去除标题栏
     return inflater.inflate(R.layout.dialog, container, false);
}
  • 设置点击外部/返回键不消失
getDialog.setCancelable(false);
getDialog.setCanceledOnTouchOutside(false);
 
getDialog().setOnKeyListener(new DialogInterface.OnKeyListener() {
            @Override
            public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
                if (keyCode == KeyEvent.KEYCODE_BACK) {
                    return true;
                }
                return false;
            }
        });
  • Enter和Exit动画 (飞入飞出)
//在style.xml中引入自定义动画

 

 
//在Java代码中设置窗口动画
getDialog().getWindow().getAttributes().windowAnimations = R.style.CustomDialog;
 
//popwin_show_anim.xml代码
<translate
     android:duration=”300″
     android:fromYDelta=”100%p”
     android:toYDelta=”0″ />
<alpha
    android:duration=”300″
    android:fromAlpha=”0.0″
    android:toAlpha=”1.0″ />
 
//popwin_hide_anim.xml代码
 
<translate
    android:duration=”300″
    android:fromYDelta=”0″
    android:toYDelta=”50%p”/>
<alpha
    android:duration=”300″
    android:fromAlpha=”1.0″
    android:toAlpha=”0.8″/>

 

关于Editext的使用

  • 当一个界面使用了多个Editext,如何确定当前获取焦点的Editext是哪一个
方法一:
private EditText mEdtInput,mCurrentEdtView;
 
mEdtInput = (EditText) findViewById(R.id.edt_input);
 
mEdtInput.setOnFocusChangeListener(mFocusChangedListener);
 
private OnFocusChangeListener mFocusChangedListener = new OnFocusChangeListener() {
        @Override
        public void onFocusChange(View view, boolean hasFocus) {
            if(hasFocus){
                mCurrentEdtView = (EditText) view;
            }
            //使用view.getId();判断
        }
    };
 
方法二:
View decorView = getWindow().getDecorView();
View focus = decorView.findFocus();
//使用view.getId();判断

  • Editext禁止自动弹出软键盘
方法-:
在Manifest.xml文件中相应的activity下添加一下代码:
android:windowSoftInputMode=”stateHidden”
方法二:
让EditText失去焦点,使用EditText的clearFocus方法
例:EditText edit=(EditText)findViewById(R.id.edit);
edit.clearFocus();
方法三:
强制隐藏Android输入法窗口
例:EditText edit=(EditText)findViewById(R.id.edit);
InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(edit.getWindowToken(),0);
方法四:
EditText始终不弹出软件键盘
例:EditText edit=(EditText)findViewById(R.id.edit);
edit.setInputType(InputType.TYPE_NULL);
方法五:
在EditText标签的外层Layout中加入focusableInTouchMode属性
例:
<LinearLayout 
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:focusableInTouchMode = “true”>
<EditText>…</EditText>
</LinearLayout >
 
  • Editext禁止自动弹出软键盘且光标不消失
1.在xml布局文件中android:inputType=”none”
2.代码中设置:
mSmsCodeInputEditText.setOnTouchListener(new OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                int inType = mSmsCodeInputEditText.getInputType();
                mSmsCodeInputEditText.setInputType(InputType.TYPE_NULL);
                mSmsCodeInputEditText.onTouchEvent(event);
                mSmsCodeInputEditText.setInputType(inType);
                CharSequence text = mSmsCodeInputEditText.getText();
                if (text instanceof Spannable) {
                    Spannable spanText = (Spannable) text;
                    Selection.setSelection(spanText, text.length());
                }
                return true;
            }
        });