Android ViewDragHelper使用介绍

ViewDragHelper是support.v4下提供的用于处理拖拽滑动的辅助类,查看Android的DrawerLayout源码,可以发现,它内部就是使用了该辅助类来处理滑动事件的.

public DrawerLayout(Context context,AttributeSet attrs,int defStyle) { 
  super(context,attrs,defStyle); 
  setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS); 
  final float density = getResources().getDisplayMetrics().density; 
  mMinDrawerMargin = (int) (MIN_DRAWER_MARGIN * density + 0.5f); 
  final float minVel = MIN_FLING_VELOCITY * density; 
  mLeftCallback = new ViewDragCallback(Gravity.LEFT); 
  mRightCallback = new ViewDragCallback(Gravity.RIGHT); 
  mLeftDragger = ViewDragHelper.create(this,TOUCH_SLOP_SENSITIVITY,mLeftCallback); 
  mLeftDragger.setEdgeTrackingEnabled(ViewDragHelper.EDGE_LEFT); 
  mLeftDragger.setMinVelocity(minVel); 
  mLeftCallback.setDragger(mLeftDragger); 
 
  mRightDragger = ViewDragHelper.create(this,mRightCallback); 
  mRightDragger.setEdgeTrackingEnabled(ViewDragHelper.EDGE_RIGHT); 
  mRightDragger.setMinVelocity(minVel); 
  mRightCallback.setDragger(mRightDragger); 
 
  //省略 
 } 

有了ViewDragHelper,我们在写与拖拽滑动相关的自定义控件的时候就变得非常简单了,例如我们可以用来实现自定义侧滑菜单,再也不需要在onTouchEvent方法里计算滑动距离来改变布局边框的位置了.

使用ViewDragHelper类的大体步骤分为3步:

步骤1.在自定义的ViewGroup子类下通过ViewDragHelper的静态方法获取到ViewDragHelper的实例引用,注意它是一个单例的.

查看源码:

/** 
 * Factory method to create a new ViewDragHelper. 
 * 
 * @param forParent Parent view to monitor 
 * @param cb Callback to provide information and receive events 
 * @return a new ViewDragHelper instance 
 */ 
 public static ViewDragHelper create(ViewGroup forParent,Callback cb) { 
  return new ViewDragHelper(forParent.getContext(),forParent,cb); 
 } 

可以发现它需要接收2个参数,参数1就是当前要使用ViewDragHelper的自定义控件的引用,Callback是一个回调抽象类,该回调接口是用于建立当前自定义控件与ViewDragHelper沟通的桥梁,Callback内定义了多个回调函数,这些回调函数涵盖了与当前自定义控件相关的是否允许拖拽,当前拖拽的View是哪一个view,拖拽的view的位置如何变化,释放的时候那个view被释放了,释放时的速度是怎么样的等等.稍后会详细介绍.

步骤2.有了ViewDragHelper的引用后,我们就需要传递相关的触摸事件给ViewDragHelper来帮我们处理,那么怎么传递呢?

可以通过重写onInterceptTouchEvent和onTouchEvent这2个函数来传递,前者是用于决定是否要拦截中断事件的,后者是用于消费触摸事件的,如果前者return true则表示事件需要被拦截,那么事件就会直接回调给onTouchEvent去处理,如果onTouchEvent返回true,则事件被消费,返回false则向上返回它的父类调用处,如果事件在向上层层返回的过程中没有被处理的话,那么事件最终将会消失;当然,如果onInterceptTouchEvent返回false的话,那么事件就会继续向下传递个它的直接子View去分发处理,关于事件分发的更多理论知识,大家可以看这篇文章事件分发机制的原理总结.

实例代码:

@Override 
public boolean onInterceptTouchEvent(MotionEvent ev) { 
 //由ViewDragHelper类来决定是否要拦截事件 
 return dragHelper.shouldInterceptTouchEvent(ev); 
} 
@Override 
public boolean onTouchEvent(MotionEvent event) { 
 try { 
  //由ViewDragHelper类来决定是否要处理触摸事件,这里可能有异常 
  dragHelper.processTouchEvent(event); 
 } catch (Exception e) { 
  e.printStackTrace(); 
 } 
 //返回true,可以持续接收到后续事件 
 return true; 
} 

步骤3:重写ViewDragHelper.Callback()的相关回调方法,处理事件,大体有如下方法:

下面将通过demo来分别介绍几个常用的方法,先来看看demo的整体代码,这是一个自定义的ViewGroup的子类,里面有2个子控件,分别是侧边栏和主体布局

/** 
 * Created by mChenys on 2015/12/16. 
 */ 
public class DragLayout extends FrameLayout { 
 private String TAG = "DragLayout"; 
 private ViewDragHelper dragHelper; 
 private LinearLayout mLeftContent; //左侧面板 
 private LinearLayout mMainContent;//主体面板 
 
 public DragLayout(Context context) { 
  this(context,null); 
 } 
 
 public DragLayout(Context context,AttributeSet attrs) { 
  this(context,0); 
 } 
 
 public DragLayout(Context context,int defStyleAttr) { 
  super(context,defStyleAttr); 
  init(); 
 } 
 //重写此方法,可以获取该容器下的所有的直接子View 
 @Override 
 protected void onFinishInflate() { 
  super.onFinishInflate(); 
  mLeftContent = (LinearLayout) getChildAt(0); 
  mMainContent = (LinearLayout) getChildAt(1); 
 } 
  
 private void init() { 
  //step1 通过ViewDragHelper的单例方法获取ViewDragHelper的实例 
  dragHelper = ViewDragHelper.create(this,mCallback); 
 } 
 //step2 传递触摸事件,需要重写onInterceptTouchEvent和onTouchEvent 
 @Override 
 public boolean onInterceptTouchEvent(MotionEvent ev) { 
  //由ViewDragHelper类来决定是否要拦截事件 
  return dragHelper.shouldInterceptTouchEvent(ev); 
 } 
 @Override 
 public boolean onTouchEvent(MotionEvent event) { 
  try { 
   //由ViewDragHelper类来决定是否要处理触摸事件 
   dragHelper.processTouchEvent(event); 
  } catch (Exception e) { 
   e.printStackTrace(); 
  } 
  //返回true,可以持续接收到后续事件 
  return true; 
 } 
 //step3 重写ViewDragHelper.Callback()的相关回调方法,处理事件 
 private ViewDragHelper.Callback mCallback = new ViewDragHelper.Callback() { 
  /** 
   * 1.改方法是abstract的方法,必须要实现,其返回结果决定当前child是否可以拖拽 
   * @param child 当前被拖拽的view 
   * @param pointerId pointerId 区分多点触摸的id 
   * @return true表示允许拖拽,false则不允许拖拽,默认返回false 
   */ 
  @Override 
  public boolean tryCaptureView(View child,int pointerId) { 
   Log.d(TAG,"tryCaptureView:当前被拖拽的view:" + child); 
   return false; 
  } 
 }; 
} 

布局文件:

<?xml version="1.0" encoding="utf-8"?> 
<mchenys.net.csdn.blog.mytencentqq.view.DragLayout xmlns:android="http://schemas.android.com/apk/res/android" 
 android:id="@+id/dragLayout" 
 android:layout_width="match_parent" 
 android:layout_height="match_parent"> 
 <!--左侧--> 
 <LinearLayout 
  android:id="@+id/layout_left" 
  android:layout_width="match_parent" 
  android:layout_height="match_parent" 
  android:background="@android:color/holo_red_light" 
  android:orientation="vertical"> 
  <TextView 
   android:layout_width="match_parent" 
   android:layout_height="match_parent" 
   android:gravity="center" 
   android:text="左侧" 
   android:textSize="30sp" /> 
 </LinearLayout> 
 <!--主体布局--> 
 <LinearLayout 
  android:id="@+id/layout_main" 
  android:layout_width="match_parent" 
  android:layout_height="match_parent" 
  android:background="@android:color/holo_blue_light" 
  android:orientation="vertical"> 
  <TextView 
   android:layout_width="match_parent" 
   android:layout_height="match_parent" 
   android:gravity="center" 
   android:text="主体" 
   android:textSize="30sp" /> 
 </LinearLayout> 
</mchenys.net.csdn.blog.mytencentqq.view.DragLayout> 

运行效果:

打印的log

D/DragLayout: tryCaptureView:当前被拖拽的view:android.widget.LinearLayout{32f4d44b V.E..... ........ 0,0-720,1134 #7f0c0052 app:id/layout_main} 
D/DragLayout: tryCaptureView:当前被拖拽的view:android.widget.LinearLayout{32f4d44b V.E..... ........ 0,1134 #7f0c0052 app:id/layout_main} 

由上面的log可以知道tryCaptureView方法被执行了,但是mMainContent却没有被拖动出来,只是为什么呢,因为tryCaptureView返回了false.默认是返回false的,下面修改为mMainContent可以拖动,mLeftContent不可以拖动:

@Override 
public boolean tryCaptureView(View child,int pointerId) { 
 Log.d(TAG,"tryCaptureView:当前被拖拽的view:" + child); 
 if (child == mMainContent) { 
  return true; //只有主题布局可以被拖动 
 } 
 return false; 
} 

运行效果还是移动不了,这是为什么呢?

这是以因为我们还没有重写clampViewPositionHorizontal方法,下面将介绍该方法的使用

/** 
 * 根据建议值修正将要移动到的横向位置,此时没有发生真正的移动 
 * @param child 当前被拖拽的View 
 * @param left 新的建议值 
 * @param dx 水平位置的变化量 
 * @return 
 */ 
@Override 
public int clampViewPositionHorizontal(View child,int left,int dx) { 
 Log.d(TAG,"clampViewPositionHorizontal:" + "旧的left坐标oldLeft:" + child.getLeft() 
   + " 水平位置的变化量dx:" + dx + " 新的建议值left:" + left); 
  
 return super.clampViewPositionHorizontal(child,left,dx); //父类默认返回0 
} 

该方法返回的是水平方向的移动建议值,该建议值等于当前的X坐标+水平方向的变化量,向右移动,偏移量为正值,向左移动则为负数.默认返回的是调用父类的重写的方法,查看源码可以发现默认返回的是0,如果建议值等于0的话,就表示水平方向不会移动.如果想要移动,我们需要返回它提供的建议值left,我们来看看运行的log:

tryCaptureView:当前被拖拽的view:android.widget.LinearLayout{23a3c537 V.E..... ........ 0,1134 #7f0c0052 app:id/layout_main} 
clampViewPositionHorizontal:旧的left坐标oldLeft:0 水平位置的变化量dx:1 新的建议值left:1 
clampViewPositionHorizontal:旧的left坐标oldLeft:0 水平位置的变化量dx:12 新的建议值left:12 
clampViewPositionHorizontal:旧的left坐标oldLeft:0 水平位置的变化量dx:63 新的建议值left:63 
clampViewPositionHorizontal:旧的left坐标oldLeft:0 水平位置的变化量dx:53 新的建议值left:53 
tryCaptureView:当前被拖拽的view:android.widget.LinearLayout{23a3c537 V.E..... ........ 0,1134 #7f0c0052 app:id/layout_main} 
clampViewPositionHorizontal:旧的left坐标oldLeft:0 水平位置的变化量dx:-5 新的建议值left:-5 
clampViewPositionHorizontal:旧的left坐标oldLeft:0 水平位置的变化量dx:-2 新的建议值left:-2 
clampViewPositionHorizontal:旧的left坐标oldLeft:0 水平位置的变化量dx:-6 新的建议值left:-6 
clampViewPositionHorizontal:旧的left坐标oldLeft:0 水平位置的变化量dx:-11 新的建议值left:-11 

由上面的log可以看出,分别是向右拖拽和向左拖拽的结果,如果我们返回了它的建议值,就可以实现水平方向的拖动了.

将clampViewPositionHorizontal的返回值修改成return left;看看运行效果:

跟我们预想的结果一样,只有主体布局可以滑动,左侧的布局不能滑动,如果想要左侧布局也可以滑动,那么只需要在tryCaptureView直接返回true即可.效果如下:

同样的,如果要实现垂直方向的拖拽滚动,就需要重新下面这个方法了.

/** 
 * 根据建议值修正将要移动到的纵向位置,此时没有发生真正的移动 
 * @param child 当前被拖拽的View 
 * @param top 新的建议值 
 * @param dy 垂直位置的变化量 
 * @return 
 */ 
@Override 
public int clampViewPositionVertical(View child,int top,int dy) { 
 Log.d(TAG,"clampViewPositionHorizontal:" + "旧的top坐标oldTop:" + child.getTop() 
   + " 垂直位置的变化量dy:" + dy + " 新的建议值top:" + top); 
 return top; 
} 

 效果如下,可以随意的滑动,实现起来是不是很简单啊

继续介绍剩下的回调方法

/** 
 * 当capturedChild被捕获时调用 
 * @param capturedChild 当前被拖拽的view 
 * @param activePointerId 
 */ 
@Override 
public void onViewCaptured(View capturedChild,int activePointerId) { 
 Log.d(TAG,"onViewCaptured:当前被拖拽的view:" + capturedChild); 
 super.onViewCaptured(capturedChild,activePointerId); 
} 

该回调方法和tryCaptureView一样都可以获取到当前被拖拽的View,不同点在于tryCaptureView是可以决定哪个View是可以被拖拽滑动的,而onViewCaptured只是用来获取当前到底哪个View被正在拖拽而已.

/** 
 * 返回拖拽的范围,不对拖拽进行真正的限制,仅仅决定了动画执行的速度 
 * @param child 
 * @return 返回一个固定值 
 */ 
@Override 
public int getViewHorizontalDragRange(View child) { 
 int range = super.getViewHorizontalDragRange(child); 
 Log.d(TAG,"被拖拽的范围getViewHorizontalDragRange:" + range); 
 return range; 
} 

该回调方法是用于决定水平方向的动画执行速度,相对的垂直方向肯定也会有相应的方法,没错,就是下面这个:

@Override 
public int getViewVerticalDragRange(View child) { 
 return super.getViewVerticalDragRange(child); 
}

那么话又说回来,我们有什么办法可以限制子View的滑动范围呢,如果范围不能很好的控制的话,那滑动也没有什么意义了.还记得上面介绍的clampViewPositionHorizontal和clampViewPositionVertical吗,分别用于设置水平方向和垂直方向的移动建议值,假设我们要限制该自定义控件的子View在水平方向移动的范围为0-屏幕宽度的0.6,那么如何控制呢.要实现这个限制,我们现在获取屏幕的宽度,由于当前的自定义控件是全屏显示的,所以直接就可以那控件的宽度来作为屏幕的宽度,那么如何获取呢?有2种方式,分别是在onMeasure和onSizeChange方法里面调用getMeasuredWidth()方法获取.前者是测量完之后获取,后者是当控件的宽高发生变化后获取,例如:

@Override 
protected void onSizeChanged(int w,int h,int oldw,int oldh) { 
 super.onSizeChanged(w,h,oldw,oldh); 
 // 当尺寸有变化的时候调用 
 mHeight = getMeasuredHeight(); 
 mWidth = getMeasuredWidth(); 
 // 移动的范围 
 mRange = (int) (mWidth * 0.6f); 
} 

接下来,在clampViewPositionHorizontal方法内部做判断,如果当前的建议值left超过了mRange,那么返回mRange,如果小于了0,则返回0,这样一来子View的滑动范围就限定在0-mRange之间了,修改代码如下:

@Override 
public int clampViewPositionHorizontal(View child,"clampViewPositionHorizontal 建议值left:" + left + " mRange:" + mRange); 
 if (left > mRange) { 
  left = mRange; 
 } else if (left < 0) { 
  left = 0; 
 } 
 return left; 
} 

如果垂直方向也想限定的话,那就修改clampViewPositionVertical返回的建议值

@Override 
public int clampViewPositionVertical(View child,"clampViewPositionVertical 建议值top:" + top + " mRange:" + mRange); 
 if (top > mRange) { 
  top = mRange; 
 } else if (top < 0) { 
  top = 0; 
 } 
 return top; 
} 

来看看运行的效果:

如此一来,我们就可以随意的控制子View的拖拽滑动的范围了.那么新的问题又来的,如果现在的需求是只能mMainContent被拖拽,mLeftContent不能被拖拽,也许你会说,这还不简单吗,直接在tryCaptureView判断当前拖拽的子View是mLeftContent的话就返回false不就得了,如果需求只是这样的话确实可以满足了,但是如果在加上一个条件,那就是拖拽mLeftContent的时候的效果相当于把mMainContent拖拽了,什么意思呢,也就说现在mMainContent已经是打开的状态了,我想通过滑动mLeftContent就能把mMainContent滑动了.而mLeftContent还是原来的位置不动.这个要怎么实现呢?

首先可以肯定的是,tryCaptureView方法必须返回true,表示mMainContent和mLeftContent都可以被滑动,接下来要处理的就是如何在mLeftContent滑动的时候是滑动mMainContent的.那么现在就要介绍另一个回调方法了,如下所示:

/** 
 * 当View的位置发生变化的时候,处理要做的事情(更新状态,伴随动画,重绘界面) 
 * 此时,View已经发生了位置的改变 
 * 
 * @param changedView 改变位置的View 
 * @param left  新的左边值 
 * @param top   新的上边值 
 * @param dx   水平方向的变化量 
 * @param dy   垂直方向的变化量 
 */ 
@Override 
public void onViewPositionChanged(View changedView,int dx,"位置发生变化onViewPositionChanged:" + "新的左边值left: " + left + " 水平方向的变化量dx:" + dx 
   + " 新的上边值top:" + top + " 垂直方向的变化量dy:" + dy); 
 super.onViewPositionChanged(changedView,top,dx,dy); 
 // 为了兼容低版本,每次修改值之后,进行重绘 
 invalidate(); 
} 

该方法是随着View的位置发生变化而不断回调的.四个参数如上面的注释所述,通过该方法可以拿到当前正在拖拽滑动的View是哪个View,有了这依据之后,我们就将在mLeftContent上的滑动的水平方向和垂直方向的变化量传递给mMainContent,这样一来,滑动mLeftContent的效果不就等于滑动mMainContent了吗.需要注意的是,该回调方法在低版本上为了兼容还需要加上invalidate();这句代码,invalidate是用来刷新界面的,他会导致界面的重绘.

滑动mMainContent来看看log

D/DragLayout: 位置发生变化onViewPositionChanged:新的左边值left: 15 水平方向的变化量dx:15 新的上边值top:8 垂直方向的变化量dy:8 
D/DragLayout: 位置发生变化onViewPositionChanged:新的左边值left: 32 水平方向的变化量dx:17 新的上边值top:16 垂直方向的变化量dy:8 
D/DragLayout: 位置发生变化onViewPositionChanged:新的左边值left: 121 水平方向的变化量dx:89 新的上边值top:46 垂直方向的变化量dy:30 
D/DragLayout: 位置发生变化onViewPositionChanged:新的左边值left: 145 水平方向的变化量dx:24 新的上边值t 
由log可以看出,最新的left和top值是等于上一次的位置+水平/垂直方向的变化量.这个特点有点类似建议值了.不同的是建议值发生了改变不代表View就一定已经处于滑动,除非返回的值也是建议值,但是onViewPositionChanged方法就不同了,这个方法只要一执行,就说明目标View是处于滑动状态的.

以水平方向滑动为例,垂直方向不移动,接下来就可以在onViewPositionChanged方法内做判断了,如下所示:

@Override 
public void onViewPositionChanged(View changedView,int dy) { 
 super.onViewPositionChanged(changedView,dy); 
 //获取最新的left坐标 
 int newLeft = left; 
 if (changedView == mLeftContent) { 
  //如果滑动的是mLeftContent,则将其水平变化量dx传递给mMainContent,记录在newLeft中 
  newLeft = mMainContent.getLeft() + dx; 
 } 
 //矫正范围 
 if (newLeft > mRange) { 
  newLeft = mRange; 
 } else if (newLeft < 0) { 
  newLeft = 0; 
 } 
 //再次判断,限制mLeftContent的滑动 
 if (changedView == mLeftContent) { 
  //强制将mLeftContent的位置摆会原来的位置,这里通过layout方法传入左,上,右,下坐标来实现 
  //当然方法不限于这一种,例如还可以通过scrollTo(x,y)方法 
  mLeftContent.layout(0,mWidth,mHeight); 
  //改变mMainContent的位置 
  mMainContent.layout(newLeft,newLeft + mWidth,mHeight); 
 } 
 // 为了兼容低版本,进行重绘 
 invalidate(); 
} 

效果图:

由上面的效果图可以发现已经可以实现当手指向右滑动mLeftContent时,滑动的效果等于向右滑动mMainContent,当同时也会发现一个问题,那就是手指在mLeftContent向左滑动的时候并没有效果,这是因为我们限制了子View的滑动范围就是0-mRange,所以,如果滑动时小于0是没有效果的.那如果我们想要实现在mLeftContent当手指有向左滑动的趋势,或者手指在mMainContent有向左滑动的趋势时,就关闭mLeftContent,让mMainContent自动向左滑动到x=0的位置,反之就是打开mLeftContent,让mMainContent滑动到x=mRange的位置,这个要怎么实现呢?首先我们要能够想到的时,这个向左滑动的趋势肯定是与手指松手后相关的,那有没有一个回调方法是与手指触摸松开相关的呢?下面将介绍另一个回调方法,如下所示:

/** 
 * 当被拖拽的View释放的时候回调,通常用于执行收尾的操作(例如执行动画) 
 * @param releasedChild 被释放的View 
 * @param xvel 水平方向的速度,向右释放为正值,向左为负值 
 * @param yvel 垂直方向的速度,向下释放为正值,向上为负值 
 */ 
@Override 
public void onViewReleased(View releasedChild,float xvel,float yvel) { 
 Log.d(TAG,"View被释放onViewReleased:" + "释放时水平速度xvel:" + xvel + " 释放时垂直速度yvel:" + yvel); 
 super.onViewReleased(releasedChild,xvel,yvel); 
} 

有了这个方法,我们就可以实现我们刚刚说到的效果了,首先我们来考虑下那些情况是和打开mLeftContent相关的,有2种情况:

1.当前水平方向的速度xvel=0,同时mMainContent的x位置是大于mRange/2.0f的.

2.当前水平方向的速度xvel>0

考虑了所有打开的情况,那么剩下的情况就是关闭mLeftContent.

具体逻辑如下:

@Override 
public void onViewReleased(View releasedChild,yvel); 
 if (xvel > 0 || (xvel == 0 && mMainContent.getLeft() > mRange / 2.0f)) { 
  //打开mLeftContent,即mMainContent的x=mRange 
  mMainContent.layout(mRange,mRange + mWidth,mHeight); 
 } else { 
  //关闭mLeftContent,即mMainContent的x=0 
  mMainContent.layout(0,mHeight); 
 } 
} 

效果图:

细心的话,可以发现上面的打开和关闭动画都是瞬间完成的,看起来效果不怎么好,如何实现平滑的打开和关闭呢?

通过ViewDragHelper的smoothSlideViewTo(View child,int finalLeft,int finalTop)方法可以实现平滑的滚动目标View到指定的left或者top坐标位置,接收3个参数,参数child表示要滑动的目标View,finalLeft和finalTop表示目标view最终平滑滑动到的位置.翻看源码,发现其实现原理是通过Scroller对象来实现的,也就说我们还需要重写computeScroll方法,不断的刷新当前界面,具体设置如下:

@Override 
public void onViewReleased(View releasedChild,即mMainContent的x=mRange 
  if (dragHelper.smoothSlideViewTo(mMainContent,mRange,0)) { 
   // 返回true代表还没有移动到指定位置,需要刷新界面. 
   // 参数传this(child所在的ViewGroup) 
   ViewCompat.postInvalidateOnAnimation(DragLayout.this); 
  } 
 } else { 
  //关闭mLeftContent,即mMainContent的x=0 
  if (dragHelper.smoothSlideViewTo(mMainContent,0)) { 
   ViewCompat.postInvalidateOnAnimation(DragLayout.this); 
  } 
 } 
@Override 
public void computeScroll() { 
 super.computeScroll(); 
 // 2. 持续平滑动画 (高频率调用) 
 if(dragHelper.continueSettling(true)){ 
  // 如果返回true,动画还需要继续执行 
  ViewCompat.postInvalidateOnAnimation(this); 
 } 
} 

效果如下:

总结

以上所述是小编给大家介绍的Android ViewDragHelper使用介绍,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对编程小技巧网站的支持!

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


更新Android SDK到3.0版本时,遇到Failed to rename directory E:\android\tools to E:\android\temp\ToolPackage.old01问题,导致无法更新,出现该问题的原因是由于3.0版本与较早的sdk版本之间文件结构有冲突,解决
Android 如何解决dialog弹出时无法捕捉Activity的back事件 在一些情况下,我们需要捕捉back键事件,然后在捕捉到的事件里写入我们需要进行的处理,通常可以采用下面三种办法捕捉到back事件: 1)重写onKeyDown或者onKeyUp方法 2)重写onBackPressed方
Android实现自定义带文字和图片的Button 在Android开发中经常会需要用到带文字和图片的button,下面来讲解一下常用的实现办法。一.用系统自带的Button实现 最简单的一种办法就是利用系统自带的Button来实现,这种方式代码量最小。在Button的属性中有一个是drawable
Android中的&quot;Unable to start activity ComponentInfo&quot;的错误 最近在做一款音乐播放器的时候,然后在调试的过程中发现一直报这个错误&quot;Unable to start activity ComponentInfo&quot;,从字面
Android 关于长按back键退出应用程序的实现最近在做一个Android上的应用,碰到一个问题就是如何实现长按back键退出应用程序。在网上查找了很多资料,发现几乎没有这样的实现,大部分在处理时是双击back键来退出应用程序。参考了一下双击back键退出应用程序的代码,网上主流的一种方法是下面
android自带的时间选择器只能精确到分,但是对于某些应用要求选择的时间精确到秒级,此时只有自定义去实现这样的时间选择器了。下面介绍一个可以精确到秒级的时间选择器。 先上效果图: 下面是工程目录: 这个控件我也是用的别人的,好像是一个老外写的,com.wheel中的WheelView是滑动控件的主
Android平台下利用zxing实现二维码开发 现在走在大街小巷都能看到二维码,而且最近由于项目需要,所以研究了下二维码开发的东西,开源的二维码扫描库主要有zxing和zbar,zbar在iPos平台上应用比较成熟,而在Android平台上主流还是用zxing库,因此这里主要讲述如何利用zxing
Android ListView的item背景色设置以及item点击无响应等相关问题 在Android开发中,listview控件是非常常用的控件,在大多数情况下,大家都会改掉listview的item默认的外观,下面讲解以下在使用listview时最常见的几个问题。1.如何改变item的背景色和按
如何向Android模拟器中导入含有中文名称的文件在进行Android开发的时候,如果需要向Android模拟器中导入文件进行测试,通过DDMS下手动导入或者在命令行下通过adb push命令是无法导入含有中文文件名的文件的。后来发现借用其他工具可以向模拟器中导入中文名称的文件,这个工具就是Ultr
Windows 下搭建Android开发环境一.下载并安装JDK版本要求JDK1.6+,下载JDK成功后进行安装,安装好后进行环境变量的配置【我的电脑】-——&gt;【属性】——&gt;【高级】 ——&gt;【环境变量】——&gt;【系统变量】中点击【新建】:变量名:CLASSPATH变量值:……
如何利用PopupWindow实现弹出菜单并解决焦点获取以及与软键盘冲突问题 在android中有时候可能要实现一个底部弹出菜单,此时可以考虑用PopupWindow来实现。下面就来介绍一下如何使用PopupWindow实现一个弹出窗。 主Activity代码:public void onCreat
解决Android中的ERROR: the user data image is used by another emulator. aborting的方法 今天调试代码的时候,突然出现这个错误,折腾了很久没有解决。最后在google上找到了大家给出的两种解决方案,下面给出这两种方法的链接博客:ht
AdvserView.java package com.earen.viewflipper; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory;
ImageView的scaleType的属性有好几种,分别是matrix(默认)、center、centerCrop、centerInside、fitCenter、fitEnd、fitStart、fitXY。 |值|说明| |:--:|:--| |center|保持原图的大小,显示在ImageVie
文章浏览阅读8.8k次,点赞9次,收藏20次。本文操作环境:win10/Android studio 3.21.环境配置 在SDK Tools里选择 CMAKE/LLDB/NDK点击OK 安装这些插件. 2.创建CMakeLists.txt文件 在Project 目录下,右键app,点击新建File文件,命名为CMakeLists.txt点击OK,创建完毕! 3.配置文件 在CMa..._link c++ project with gradle
文章浏览阅读1.2w次,点赞15次,收藏69次。实现目的:由mainActivity界面跳转到otherActivity界面1.写好两个layout文件,activity_main.xml和otherxml.xmlactivity_main.xml&lt;?xml version="1.0" encoding="utf-8"?&gt;&lt;RelativeLayout ="http://schemas..._android studio 界面跳转
文章浏览阅读3.8w次。前言:最近在找Android上的全局代理软件来用,然后发现了这两款神作,都是外国的软件,而且都是开源的软件,因此把源码下载了下来,给有需要研究代理这方面的童鞋看看。不得不说,国外的开源精神十分浓,大家相互使用当前基础的开源软件,然后组合成一个更大更强的大开源软件。好吧,废话不多说,下面简单介绍一下这两款开源项目。一、ProxyDroid:ProxyDroid功能比较强大,用到的技术也比较多,源码也_proxydroid
文章浏览阅读2.5w次,点赞17次,收藏6次。创建项目后,运行项目时Gradle Build 窗口却显示错误:程序包R不存在通常情况下是不会出现这个错误的。我是怎么遇到这个错误的呢?第一次创建项目,company Domain我使用的是:aven.com,但是创建过程在卡在了Building 'Calculator' Gradle Project info这个过程中,于是我选择了“Cancel”第二次创建项目,我还是使用相同的项目名称和项目路_r不存在
文章浏览阅读8.9w次,点赞4次,收藏43次。前言:在Android上使用系统自带的代理,限制灰常大,仅支持系统自带的浏览器。这样像QQ、飞信、微博等这些单独的App都不能使用系统的代理。如何让所有软件都能正常代理呢?ProxyDroid这个软件能帮你解决!使用方法及步骤如下:一、推荐从Google Play下载ProxyDroid,目前最新版本是v2.6.6。二、对ProxyDroid进行配置(基本配置:) (1) Auto S_proxydroid使用教程
文章浏览阅读1.1w次,点赞4次,收藏17次。Android Studio提供了一个很实用的工具Android设备监视器(Android device monitor),该监视器中最常用的一个工具就是DDMS(Dalvik Debug Monitor Service),是 Android 开发环境中的Dalvik虚拟机调试监控服务。可以进行的操作有:为测试设备截屏,查看特定进程中正在运行的线程以及堆栈信息、Logcat、广播状态信息、模拟电话_安卓摄像头调试工具