찾다
Javajava지도 시간Android는 ViewDragHelper를 사용하여 최신 버전의 QQ6.X 측면 슬라이딩 인터페이스 효과 예제 코드를 구현합니다.

(1) 서문:

QQ는 지난 이틀 동안 대대적인 업데이트를 거쳤습니다. (6. 일부 변경 사항이 있어서 오늘은 사이드 슬라이딩 인터페이스 효과를 구현해 보겠습니다. QQ6.X 버전. 현재도 이를 구현하기 위해 ViewDragHelper를 사용하고 있습니다.

이 예제의 특정 코드는 아래 프로젝트에 업로드되어 있습니다.

https://github.com/jiangqqlmj/DragHelper4QQ
FastDev4Android 프레임워크 프로젝트 주소: https://github.com/jiangqqlmj/FastDev4Android

(이 ).ViewGragHelper 기본 사용

앞에서 ViewGragHelper의 기본 사용법을 배웠고 내부의 여러 메서드 사용법도 알고 있었습니다. 이제 기본 사용 단계를 검토하겠습니다. ViewGragHelper를 사용하여 하위 뷰의 드래그 앤 드롭 이동을 구현하는 단계는 다음과 같습니다.

ViewGragHelper 인스턴스 생성(콜백 전달)

이벤트 차단 처리 다시 작성 onInterceptTouch 및 onTouchEvent 메소드

콜백 구현, 관련 메소드 tryCaptureView 및 수평 또는 수직 이동 거리 메소드 구현

자세한 분석은 이전 블로그를 참조하세요. 아니면 오늘 여기에서 자세한 분석을 통과하겠습니다. 예를 들어 설명해주세요.

(3).QQ5의 사이드슬립 효과 구현에 대한 분석 >

위를 살펴보면 두 개의 View로 이해할 수 있는데, 하나는 왼쪽의 하단 기능 View이고, 다른 하나는 상단의 주요 기능 내용인 View입니다. 상단 View를 드래그하거나 좌우로 슬라이드하면 상단과 하단 View가 슬라이드되며 View의 크기도 변경되며 해당 애니메이션이 추가됩니다. 물론 상단 보기를 클릭하여 측면 슬라이딩 메뉴를 열거나 닫을 수도 있습니다.

(4) 측면 슬라이딩 효과의 사용자 정의 구성 요소 구현Android는 ViewDragHelper를 사용하여 최신 버전의 QQ6.X 측면 슬라이딩 인터페이스 효과 예제 코드를 구현합니다.

1. 먼저 FrameLayout을 통합하여 사용자 정의 View DragLayout을 만듭니다. 내부적으로 정의된 일부 변수는 다음과 같습니다(주로 일부 구성 클래스, 제스처, ViewDragHelper 인스턴스, 화면 너비 및 높이, 하위 뷰 드래그 등 포함)

//是否带有阴影效果 
private boolean isShowShadow = true; 
//手势处理类 
private GestureDetectorCompat gestureDetector; 
//视图拖拽移动帮助类 
private ViewDragHelper dragHelper; 
//滑动监听器 
private DragListener dragListener; 
//水平拖拽的距离 
private int range; 
//宽度 
private int width; 
//高度 
private int height; 
//main视图距离在ViewGroup距离左边的距离 
private int mainLeft; 
private Context context; 
private ImageView iv_shadow; 
//左侧布局 
private RelativeLayout vg_left; 
//右侧(主界面布局) 
private CustomRelativeLayout vg_main;

그리고 주로 드래그를 처리하기 위한 콜백 인터페이스도 내부적으로 정의됩니다. . 드래그 프로세스 중 일부 페이지 열기, 닫기 및 슬라이딩 이벤트 콜백:

/** 
* 滑动相关回调接口 
*/
public interface DragListener { 
//界面打开 
public void onOpen(); 
//界面关闭 
public void onClose(); 
//界面滑动过程中 
public void onDrag(float percent); 
}

2. ViewGragHelper의 정적 메서드를 사용하여 사용자 정의 View DragLayout이 초기화될 때 계속 생성되는 ViewDragHelper 인스턴스 생성을 시작합니다.

public DragLayout(Context context,AttributeSet attrs, int defStyle) { 
super(context, attrs, defStyle); 
gestureDetector = new GestureDetectorCompat(context, new YScrollDetector()); 
dragHelper =ViewDragHelper.create(this, dragHelperCallback); 
}

create() 메소드가 생성될 때 dragHelperCallBack 콜백 클래스가 전달되었으며 이에 대해서는 네 번째 지점에서 논의됩니다.

3. 그런 다음 하위 뷰를 드래그하고 이동하는 목적을 달성하려면 ViewGroup에서 이벤트 메서드를 다시 작성하고 터치 이벤트를 가로채서 ViewGragHelper에서 처리해야 합니다.

/** 
* 拦截触摸事件 
* @param ev 
* @return 
*/
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) { 
return dragHelper.shouldInterceptTouchEvent(ev) &&gestureDetector.onTouchEvent(ev); 
} 
/** 
* 将拦截的到事件给ViewDragHelper进行处理 
* @param e 
* @return 
*/
@Override
public boolean onTouchEvent(MotionEvent e){ 
try { 
dragHelper.processTouchEvent(e); 
} catch (Exception ex) { 
ex.printStackTrace(); 
} 
return false; 
}

여기서는 onInterceptTouchEvent에서 이벤트를 부모 컨트롤에서 자식 View로 전송한 다음 onTouchEvent 메서드에서 가로채서 ViewDragHelper가 소비 처리를 수행하도록 합니다.


4. ViewDragHelper.Callback 인스턴스 생성을 사용자 정의하기 시작하면 dragHelperCallback은 각각 추상 메서드 tryCaptureView를 구현하고 다음 메서드를 재정의하여 측면 슬라이딩 기능을 구현합니다. 하나씩.

/** 
* 拦截所有的子View 
* @param child Child the user is attempting to capture 
* @param pointerId ID of the pointer attempting the capture 
* @return 
*/
@Override
public boolean tryCaptureView(Viewchild, int pointerId) { 
return true; 
}

이제 ViewGroup의 모든 하위 뷰(이 경우 DragLayout)를 가로채고 모든 하위 뷰를 드래그하고 이동할 수 있음을 나타내는 true를 직접 반환해야 합니다.

/** 
* 水平方向移动 
* @param child Child view beingdragged 
* @param left Attempted motion alongthe X axis 
* @param dx Proposed change inposition for left 
* @return 
*/
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) { 
if (mainLeft + dx < 0) { 
return 0; 
} else if (mainLeft + dx >range) { 
return range; 
} else { 
return left; 
} 
}

동시에 수평 슬라이딩을 표현하기 위해 이 메소드를 구현합니다. 예를 들어 위의 메인 뷰가 경계 밖으로 왼쪽으로 이동하면 바로 경계 값이 결정됩니다. 0을 반환합니다. 이는 가장 왼쪽 방향이 x=0일 수 있음을 의미합니다. 그런 다음 오른쪽으로 이동하면 범위에 도달하기 위한 오른쪽 거리가 결정됩니다. 범위 초기화에 대해서는 나중에 설명합니다. 이 두 가지 상황을 제외하고는 바로 왼쪽으로 돌아갑니다.

/** 
* 设置水平方向滑动的最远距离 
*@param child Child view to check 屏幕宽度 
* @return 
*/
@Override
public int getViewHorizontalDragRange(View child) { 
return width; 
}

이 메소드는 콜백 내에서 기본적으로 0을 반환하기 때문에 구현해야 합니다. 즉, 뷰의 클릭 이벤트가 true인 경우 전체 하위 뷰는 작동할 수 없습니다. 끌려서 이동합니다. 그런 다음 왼쪽 뷰의 너비가 여기에 직접 반환됩니다. 이는 수평 슬라이딩의 가장 먼 거리를 나타냅니다.

/** 
* 当拖拽的子View,手势释放的时候回调的方法, 然后根据左滑或者右滑的距离进行判断打开或者关闭 
* @param releasedChild 
* @param xvel 
* @param yvel 
*/
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) { 
super.onViewReleased(releasedChild,xvel, yvel); 
if (xvel > 0) { 
open(); 
} else if (xvel < 0) { 
close(); 
} else if (releasedChild == vg_main&& mainLeft > range * 0.3) { 
open(); 
} else if (releasedChild == vg_left&& mainLeft > range * 0.7) { 
open(); 
} else { 
close(); 
} 
}

이 메서드는 하위 뷰를 드래그하는 동안 손가락을 놓으면 호출되며, 이는 왼쪽 또는 오른쪽으로 이동하려는 의도를 결정하고 맨 뷰(위쪽 뷰)를 열거나 닫습니다. 마지막으로 구현된 메소드는 다음과 같습니다. onViewPositionChanged

/** 
* 子View被拖拽 移动的时候回调的方法 
* @param changedView View whoseposition changed 
* @param left New X coordinate of theleft edge of the view 
* @param top New Y coordinate of thetop edge of the view 
* @param dx Change in X position fromthe last call 
* @param dy Change in Y position fromthe last call 
*/
@Override
public void onViewPositionChanged(View changedView, int left, int top, 
int dx, int dy) { 
if (changedView == vg_main) { 
mainLeft = left; 
} else { 
mainLeft = mainLeft + left; 
} 
if (mainLeft < 0) { 
mainLeft = 0; 
} else if (mainLeft > range) { 
mainLeft = range; 
} 
if (isShowShadow) { 
iv_shadow.layout(mainLeft, 0,mainLeft + width, height); 
} 
if (changedView == vg_left) { 
vg_left.layout(0, 0, width,height); 
vg_main.layout(mainLeft, 0,mainLeft + width, height); 
} 
dispatchDragEvent(mainLeft); 
} 
};

이 메소드는 하위 View를 드래그하여 이동하는 과정에서 다시 호출되며, 이동한 좌표 위치에 따라 왼쪽 뷰와 메인 뷰를 재정의합니다. 동시에 dispathDragEvent() 메서드가 호출되어 드래그 이벤트를 처리 및 배포하고 상태에 따라 인터페이스를 다시 호출합니다.

/** 
* 进行处理拖拽事件 
* @param mainLeft 
*/
private void dispatchDragEvent(int mainLeft) { 
if (dragListener == null) { 
return; 
} 
float percent = mainLeft / (float)range; 
//根据滑动的距离的比例 
animateView(percent); 
//进行回调滑动的百分比 
dragListener.onDrag(percent); 
Status lastStatus = status; 
if (lastStatus != getStatus()&& status == Status.Close) { 
dragListener.onClose(); 
} else if (lastStatus != getStatus()&& status == Status.Open) { 
dragListener.onOpen(); 
} 
}

이 메서드에는 float 퍼센트=mainLeft/( float)range; 백분율을 계산한 후 사용됩니다.

5. 하위 뷰 레이아웃의 초기화 및 너비, 높이 및 수평 슬라이딩 거리의 크기 설정 방법은 다음과 같습니다.

/** 
* 布局加载完成回调 
* 做一些初始化的操作 
*/
@Override
protected void onFinishInflate() { 
super.onFinishInflate(); 
if (isShowShadow) { 
iv_shadow = new ImageView(context); 
iv_shadow.setImageResource(R.mipmap.shadow); 
LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); 
addView(iv_shadow, 1, lp); 
} 
//左侧界面 
vg_left = (RelativeLayout)getChildAt(0); 
//右侧(主)界面 
vg_main = (CustomRelativeLayout)getChildAt(isShowShadow ? 2 : 1); 
vg_main.setDragLayout(this); 
vg_left.setClickable(true); 
vg_main.setClickable(true); 
}

및 컨트롤 크기 변경 시 콜백 메서드:

@Override
protected void onSizeChanged(int w, int h,int oldw, int oldh) { 
super.onSizeChanged(w, h, oldw, oldh); 
width = vg_left.getMeasuredWidth(); 
height = vg_left.getMeasuredHeight(); 
//可以水平拖拽滑动的距离 一共为屏幕宽度的80% 
range = (int) (width *0.8f); 
}

이 메서드를 사용하면 너비와 높이뿐 아니라 드래그 수평 거리도 실시간으로 얻을 수 있습니다.

6.上面的所有核心代码都为使用ViewDragHelper实现子控件View拖拽移动的方法,但是根据我们这边侧滑效果还需要实现动画以及滑动过程中View的缩放效果,所以我们这边引入了一个动画开源库:

Android는 ViewDragHelper를 사용하여 최신 버전의 QQ6.X 측면 슬라이딩 인터페이스 효과 예제 코드를 구현합니다.

然后根据前面算出来的百分比来缩放View视图:

/** 
* 根据滑动的距离的比例,进行平移动画 
* @param percent 
*/
private void animateView(float percent) { 
float f1 = 1 - percent * 0.5f; 
ViewHelper.setTranslationX(vg_left,-vg_left.getWidth() / 2.5f + vg_left.getWidth() / 2.5f * percent); 
if (isShowShadow) { 
//阴影效果视图大小进行缩放 
ViewHelper.setScaleX(iv_shadow, f1* 1.2f * (1 - percent * 0.10f)); 
ViewHelper.setScaleY(iv_shadow, f1* 1.85f * (1 - percent * 0.10f)); 
} 
}

7.当然除了上面这些还缺少一个效果就是,当我们滑动过程中假如我们手指释放,按照常理来讲view就不会在进行移动了,那么这边我们需要一个加速度当我们释放之后,还能保持一定的速度,该怎么样实现呢?答案就是实现computeScroll()方法。

/** 
* 有加速度,当我们停止滑动的时候,该不会立即停止动画效果 
*/
@Override
public void computeScroll() { 
if (dragHelper.continueSettling(true)){ 
ViewCompat.postInvalidateOnAnimation(this); 
} 
}

OK上面关于DragLayout的核心代码就差不多这么多了,下面是使用DragLayout类来实现侧滑效果啦!

(五).侧滑效果组件使用

1.首先使用的布局文件如下:

<com.chinaztt.widget.DragLayout 
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/dl"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/transparent"
> 
<!--下层 左边的布局--> 
<includelayoutincludelayout="@layout/left_view_layout"/> 
<!--上层 右边的主布局--> 
<com.chinaztt.widget.CustomRelativeLayout 
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FFFFFF"
> 
<LinearLayout 
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
> 
<RelativeLayout 
android:id="@+id/rl_title"
android:layout_width="match_parent"
android:layout_height="49dp"
android:gravity="bottom"
android:background="@android:color/holo_orange_light"
> 
<includelayoutincludelayout="@layout/common_top_bar_layout"/> 
</RelativeLayout> 
<!--中间内容后面放入Fragment--> 
<FrameLayout 
android:layout_width="fill_parent"
android:layout_height="fill_parent"
> 
<fragment 
android:id="@+id/main_info_fragment"
class="com.chinaztt.fragment.OneFragment"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/> 
</FrameLayout> 
</LinearLayout> 
</com.chinaztt.widget.CustomRelativeLayout> 
</com.chinaztt.widget.DragLayout>

该布局文件中父层View就是DragLayout,然后内部有两个RelativeLayout布局,分别充当下一层布局和上一层主布局。

2.下面我们来看一下下层菜单布局,这边我专门写了一个left_view_layout.xml文件,其中主要分为三块,第一块顶部为头像个人基本信息布局,中间为功能入口列表,底部是设置等功能,具体布局代码如下:

<RelativeLayout 
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="70dp"
android:background="@drawable/sidebar_bg"
> 
<LinearLayout 
android:id="@+id/ll1"
android:paddingLeft="30dp"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical"> 
<!--头像,昵称信息--> 
<LinearLayout 
android:layout_width="match_parent"
android:layout_height="70dp"
android:orientation="horizontal"
android:gravity="center_vertical"
> 
<com.chinaztt.widget.RoundAngleImageView 
android:id="@+id/iv_bottom"
android:layout_width="50dp"
android:layout_height="50dp"
android:scaleType="fitXY"
android:src="@drawable/icon_logo"
app:roundWidth="25dp"
app:roundHeight="25dp"/> 
<LinearLayout 
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:layout_gravity="center_vertical"
android:orientation="vertical"> 
<RelativeLayout 
android:layout_width="fill_parent"
android:layout_height="wrap_content"
> 
<TextView 
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="15dp"
android:text="名字:jiangqqlmj"
android:textColor="@android:color/black"
android:textSize="15sp" /> 
<ImageButton 
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginRight="100dp"
android:layout_width="22dp"
android:layout_height="22dp"
android:background="@drawable/qrcode_selector"/> 
</RelativeLayout> 
<TextView 
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="15dp"
android:text="QQ:781931404"
android:textColor="@android:color/black"
android:textSize="13sp" /> 
</LinearLayout> 
</LinearLayout> 
<LinearLayout 
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"> 
<ImageView 
android:layout_width="17dp"
android:layout_height="17dp"
android:scaleType="fitXY"
android:src="@drawable/sidebar_signature_nor"/> 
<TextView 
android:layout_marginLeft="5dp"
android:textSize="13sp"
android:textColor="#676767"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="用心做产品!"/> 
</LinearLayout> 
</LinearLayout> 
<!--底部功能条--> 
<includelayoutincludelayout="@layout/left_view_bottom_layout"
android:id="@+id/bottom_view"
/> 
<!--中间列表--> 
<ListView 
android:id="@+id/lv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="@id/bottom_view"
android:layout_below="@id/ll1"
android:layout_marginBottom="30dp"
android:layout_marginTop="70dp"
android:cacheColorHint="#00000000"
android:listSelector="@drawable/lv_click_selector"
android:divider="@null"
android:scrollbars="none"
android:textColor="#ffffff"/> 
</RelativeLayout>

该布局还是比较简单的,对于上层主内容布局这边就放一个顶部导航栏和中的Fragment内容信息,留着后期大家功能扩展即可。

3.主Activity使用如下:

public class MainActivity extends BaseActivity { 
private DragLayout dl; 
private ListView lv; 
private ImageView iv_icon, iv_bottom; 
private QuickAdapter<ItemBean> quickAdapter; 
@Override
protected void onCreate(Bundle savedInstanceState) { 
super.onCreate(savedInstanceState); 
setContentView(R.layout.activity_main); 
setStatusBar(); 
initDragLayout(); 
initView(); 
} 
private void initDragLayout() { 
dl = (DragLayout) findViewById(R.id.dl); 
dl.setDragListener(new DragLayout.DragListener() { 
//界面打开的时候 
@Override
public void onOpen() { 
} 
//界面关闭的时候 
@Override
public void onClose() { 
} 
//界面滑动的时候 
@Override
public void onDrag(float percent) { 
ViewHelper.setAlpha(iv_icon, 1 - percent); 
} 
}); 
} 
private void initView() { 
iv_icon = (ImageView) findViewById(R.id.iv_icon); 
iv_bottom = (ImageView) findViewById(R.id.iv_bottom); 
lv = (ListView) findViewById(R.id.lv); 
lv.setAdapter(quickAdapter=new QuickAdapter<ItemBean>(this,R.layout.item_left_layout, ItemDataUtils.getItemBeans()) { 
@Override
protected void convert(BaseAdapterHelper helper, ItemBean item) { 
helper.setImageResource(R.id.item_img,item.getImg()) 
.setText(R.id.item_tv,item.getTitle()); 
} 
}); 
lv.setOnItemClickListener(new OnItemClickListener() { 
@Override
public void onItemClick(AdapterView<?> arg0, View arg1, 
int position, long arg3) { 
Toast.makeText(MainActivity.this,"Click Item "+position,Toast.LENGTH_SHORT).show(); 
} 
}); 
iv_icon.setOnClickListener(new OnClickListener() { 
@Override
public void onClick(View arg0) { 
dl.open(); 
} 
}); 
} 
}

初始化控件,设置滑动监听器,以及左侧菜单功能列表设置即可了,不过上面大家应该看了QuickAdapter的使用,该为BaseAdapterHelper框架使用,我们需要在项目build.gradle中作如下配置:

Android는 ViewDragHelper를 사용하여 최신 버전의 QQ6.X 측면 슬라이딩 인터페이스 효과 예제 코드를 구현합니다.

具体关于BaseAdapter的使用讲解博客地址如下:

4.正式运行效果如下:

5.因为这边底层需要ViewDragHelper类,所以大家在使用的时候需要导入V4包的,不过我这边直接把ViewGragHelper类的源代码复制到项目中了。

Android는 ViewDragHelper를 사용하여 최신 버전의 QQ6.X 측면 슬라이딩 인터페이스 효과 예제 코드를 구현합니다.

(六).DragLayout源代码带注释 

上面主要分析DragLayout的具体实现,不过我这边也贴一下DragLayout带有注释的全部源代码让大家可以更好的了解DragLayout的具体实现代码:

/** 
*使用ViewRragHelper实现侧滑效果功能 
*/
publicclass DragLayout extends FrameLayout { 
private boolean isShowShadow = true; 
//手势处理类 
private GestureDetectorCompat gestureDetector; 
//视图拖拽移动帮助类 
private ViewDragHelper dragHelper; 
//滑动监听器 
private DragListener dragListener; 
//水平拖拽的距离 
private int range; 
//宽度 
private int width; 
//高度 
private int height; 
//main视图距离在ViewGroup距离左边的距离 
private int mainLeft; 
private Context context; 
private ImageView iv_shadow; 
//左侧布局 
private RelativeLayout vg_left; 
//右侧(主界面布局) 
private CustomRelativeLayout vg_main; 
//页面状态 默认为关闭 
private Status status = Status.Close; 
public DragLayout(Context context) { 
this(context, null); 
} 
public DragLayout(Context context,AttributeSet attrs) { 
this(context, attrs, 0); 
this.context = context; 
} 
public DragLayout(Context context,AttributeSet attrs, int defStyle) { 
super(context, attrs, defStyle); 
gestureDetector = new GestureDetectorCompat(context, new YScrollDetector()); 
dragHelper =ViewDragHelper.create(this, dragHelperCallback); 
} 
class YScrollDetector extends SimpleOnGestureListener { 
@Override
public boolean onScroll(MotionEvent e1,MotionEvent e2, float dx, float dy) { 
return Math.abs(dy) <=Math.abs(dx); 
} 
} 
/** 
* 实现子View的拖拽滑动,实现Callback当中相关的方法 
*/
private ViewDragHelper.Callback dragHelperCallback = new ViewDragHelper.Callback() { 
/** 
* 水平方向移动 
* @param child Child view beingdragged 
* @param left Attempted motion alongthe X axis 
* @param dx Proposed change inposition for left 
* @return 
*/
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) { 
if (mainLeft + dx < 0) { 
return 0; 
} else if (mainLeft + dx >range) { 
return range; 
} else { 
return left; 
} 
} 
/** 
* 拦截所有的子View 
* @param child Child the user isattempting to capture 
* @param pointerId ID of the pointerattempting the capture 
* @return 
*/
@Override
public boolean tryCaptureView(View child, int pointerId) { 
return true; 
} 
/** 
* 设置水平方向滑动的最远距离 
*@param child Child view to check 屏幕宽度 
* @return 
*/
@Override
public int getViewHorizontalDragRange(View child) { 
return width; 
} 
/** 
* 当拖拽的子View,手势释放的时候回调的方法, 然后根据左滑或者右滑的距离进行判断打开或者关闭 
* @param releasedChild 
* @param xvel 
* @param yvel 
*/
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) { 
super.onViewReleased(releasedChild,xvel, yvel); 
if (xvel > 0) { 
open(); 
} else if (xvel < 0) { 
close(); 
} else if (releasedChild == vg_main&& mainLeft > range * 0.3) { 
open(); 
} else if (releasedChild == vg_left&& mainLeft > range * 0.7) { 
open(); 
} else { 
close(); 
} 
} 
/** 
* 子View被拖拽 移动的时候回调的方法 
* @param changedView View whoseposition changed 
* @param left New X coordinate of theleft edge of the view 
* @param top New Y coordinate of thetop edge of the view 
* @param dx Change in X position fromthe last call 
* @param dy Change in Y position fromthe last call 
*/
@Override
public void onViewPositionChanged(View changedView, int left, int top, 
int dx, int dy) { 
if (changedView == vg_main) { 
mainLeft = left; 
} else { 
mainLeft = mainLeft + left; 
} 
if (mainLeft < 0) { 
mainLeft = 0; 
} else if (mainLeft > range) { 
mainLeft = range; 
} 
if (isShowShadow) { 
iv_shadow.layout(mainLeft, 0,mainLeft + width, height); 
} 
if (changedView == vg_left) { 
vg_left.layout(0, 0, width,height); 
vg_main.layout(mainLeft, 0,mainLeft + width, height); 
} 
dispatchDragEvent(mainLeft); 
} 
}; 
/** 
* 滑动相关回调接口 
*/
public interface DragListener { 
//界面打开 
public void onOpen(); 
//界面关闭 
public void onClose(); 
//界面滑动过程中 
public void onDrag(float percent); 
} 
public void setDragListener(DragListener dragListener) { 
this.dragListener = dragListener; 
} 
/** 
* 布局加载完成回调 
* 做一些初始化的操作 
*/
@Override
protected void onFinishInflate() { 
super.onFinishInflate(); 
if (isShowShadow) { 
iv_shadow = new ImageView(context); 
iv_shadow.setImageResource(R.mipmap.shadow); 
LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); 
addView(iv_shadow, 1, lp); 
} 
//左侧界面 
vg_left = (RelativeLayout)getChildAt(0); 
//右侧(主)界面 
vg_main = (CustomRelativeLayout)getChildAt(isShowShadow ? 2 : 1); 
vg_main.setDragLayout(this); 
vg_left.setClickable(true); 
vg_main.setClickable(true); 
} 
public ViewGroup getVg_main() { 
return vg_main; 
} 
public ViewGroup getVg_left() { 
return vg_left; 
} 
@Override
protected void onSizeChanged(int w, int h,int oldw, int oldh) { 
super.onSizeChanged(w, h, oldw, oldh); 
width = vg_left.getMeasuredWidth(); 
height = vg_left.getMeasuredHeight(); 
//可以水平拖拽滑动的距离 一共为屏幕宽度的80% 
range = (int) (width * 0.8f); 
} 
/** 
* 调用进行left和main 视图进行位置布局 
* @param changed 
* @param left 
* @param top 
* @param right 
* @param bottom 
*/
@Override
protected void onLayout(boolean changed,int left, int top, int right, int bottom) { 
vg_left.layout(0, 0, width, height); 
vg_main.layout(mainLeft, 0, mainLeft +width, height); 
} 
/** 
* 拦截触摸事件 
* @param ev 
* @return 
*/
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) { 
returndragHelper.shouldInterceptTouchEvent(ev) &&gestureDetector.onTouchEvent(ev); 
} 
/** 
* 将拦截的到事件给ViewDragHelper进行处理 
* @param e 
* @return 
*/
@Override
public boolean onTouchEvent(MotionEvent e){ 
try { 
dragHelper.processTouchEvent(e); 
} catch (Exception ex) { 
ex.printStackTrace(); 
} 
return false; 
} 
/** 
* 进行处理拖拽事件 
* @param mainLeft 
*/
private void dispatchDragEvent(intmainLeft) { 
if (dragListener == null) { 
return; 
} 
float percent = mainLeft / (float)range; 
//滑动动画效果 
animateView(percent); 
//进行回调滑动的百分比 
dragListener.onDrag(percent); 
Status lastStatus = status; 
if (lastStatus != getStatus()&& status == Status.Close) { 
dragListener.onClose(); 
} else if (lastStatus != getStatus()&& status == Status.Open) { 
dragListener.onOpen(); 
} 
} 
/** 
* 根据滑动的距离的比例,进行平移动画 
* @param percent 
*/
private void animateView(float percent) { 
float f1 = 1 - percent * 0.5f; 
ViewHelper.setTranslationX(vg_left,-vg_left.getWidth() / 2.5f + vg_left.getWidth() / 2.5f * percent); 
if (isShowShadow) { 
//阴影效果视图大小进行缩放 
ViewHelper.setScaleX(iv_shadow, f1* 1.2f * (1 - percent * 0.10f)); 
ViewHelper.setScaleY(iv_shadow, f1* 1.85f * (1 - percent * 0.10f)); 
} 
} 
/** 
* 有加速度,当我们停止滑动的时候,该不会立即停止动画效果 
*/
@Override
public void computeScroll() { 
if (dragHelper.continueSettling(true)){ 
ViewCompat.postInvalidateOnAnimation(this); 
} 
} 
/** 
* 页面状态(滑动,打开,关闭) 
*/
public enum Status { 
Drag, Open, Close 
} 
/** 
* 页面状态设置 
* @return 
*/
public Status getStatus() { 
if (mainLeft == 0) { 
status = Status.Close; 
} else if (mainLeft == range) { 
status = Status.Open; 
} else { 
status = Status.Drag; 
} 
return status; 
} 
public void open() { 
open(true); 
} 
public void open(boolean animate) { 
if (animate) { 
//继续滑动 
if(dragHelper.smoothSlideViewTo(vg_main, range, 0)) { 
ViewCompat.postInvalidateOnAnimation(this); 
} 
} else { 
vg_main.layout(range, 0, range * 2,height); 
dispatchDragEvent(range); 
} 
} 
public void close() { 
close(true); 
} 
public void close(boolean animate) { 
if (animate) { 
//继续滑动 
if(dragHelper.smoothSlideViewTo(vg_main, 0, 0)) { 
ViewCompat.postInvalidateOnAnimation(this); 
} 
} else { 
vg_main.layout(0, 0, width,height); 
dispatchDragEvent(0); 
} 
} 
}

(七).最后总结

今天我们实现打造QQ最新版本QQ6.X效果,同时里边用到了ViewDragHelper,BaseAdapterHelper的运用,具体该知识点的使用方法,我已经在我的博客中更新讲解的文章,欢迎大家查看。

更多Android는 ViewDragHelper를 사용하여 최신 버전의 QQ6.X 측면 슬라이딩 인터페이스 효과 예제 코드를 구현합니다.相关文章请关注PHP中文网!

성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
Java 프로그래머는 오디오 및 비디오 개발을 어떻게 시작합니까? C를 배워야합니까?Java 프로그래머는 오디오 및 비디오 개발을 어떻게 시작합니까? C를 배워야합니까?Apr 19, 2025 pm 06:39 PM

Java 프로그래머는 오디오 및 비디오 개발을 어떻게 시작합니까? 비디오 프로젝트에 참여하고 있지만 오디오 및 비디오 기술에 관심이있는 경우 Java 프로그래머로 권장되는 학습 리소스 ...

POI-TL이 이중 y 축 줄 차트를 그릴 때 왜 라인의 수가 두 배가됩니까?POI-TL이 이중 y 축 줄 차트를 그릴 때 왜 라인의 수가 두 배가됩니까?Apr 19, 2025 pm 06:33 PM

POI-TL이 이중 y 축 줄 차트를 그릴 때 라인 수를 두 배로 늘 렸습니다. POI-TL 라이브러리를 사용하여 두 y 축이 포함 된 라인 차트를 그릴 때 이상한 문제가 발생했습니다. � ...

비즈니스 모듈의 엔티티, 매퍼 및 서비스를 스프링 클라우드 알리바바의 공통 모듈로 중앙 집중화하는 방법은 무엇입니까?비즈니스 모듈의 엔티티, 매퍼 및 서비스를 스프링 클라우드 알리바바의 공통 모듈로 중앙 집중화하는 방법은 무엇입니까?Apr 19, 2025 pm 06:30 PM

SpringCloudalibaba의 공통 모듈로 비즈니스 모듈의 엔티티, 매퍼 및 서비스를 중앙 집중화하는 방법은 무엇입니까? SpringCloud 사용 ...

GO에서 National Secret SM4 및 SM2 알고리즘의 암호화, 암호 해독 및 상호 연결을 구현하는 방법은 무엇입니까?GO에서 National Secret SM4 및 SM2 알고리즘의 암호화, 암호 해독 및 상호 연결을 구현하는 방법은 무엇입니까?Apr 19, 2025 pm 06:27 PM

Go Language는 GO 언어에서 SM4 및 SM2의 암호화 및 암호 해독을 구현합니다. 이 기사는 Go Language를 사용하여 GO Language의 암호화 및 SM2 알고리즘의 암호화 및 암호 해독 프로세스를 구현하여 Java의 요구를 충족시키는 방법을 자세히 소개합니다.

파이썬 프로젝트에서 레이어링을해야합니까?파이썬 프로젝트에서 레이어링을해야합니까?Apr 19, 2025 pm 06:24 PM

파이썬 프로젝트에 묶을 필요가 있습니까? 최근에 Python을 배우고있을 때 많은 Django 오픈 소스 프로젝트가 Views Function에서 많은 것을 썼다는 것을 알았습니다.

맵 구조를 사용하여 시스템 도킹에서 필드 매핑 문제를 단순화하는 방법은 무엇입니까?맵 구조를 사용하여 시스템 도킹에서 필드 매핑 문제를 단순화하는 방법은 무엇입니까?Apr 19, 2025 pm 06:21 PM

시스템 도킹의 필드 매핑 처리 시스템 도킹을 수행 할 때 어려운 문제가 발생합니다. 시스템의 인터페이스 필드를 효과적으로 매핑하는 방법 ...

Intellij Idea는 Javaagent 및 RMI 기술을 통해 Spring Boot 프로젝트의 포트 번호를 어떻게 식별합니까?Intellij Idea는 Javaagent 및 RMI 기술을 통해 Spring Boot 프로젝트의 포트 번호를 어떻게 식별합니까?Apr 19, 2025 pm 06:18 PM

Intellijidea는 SpringBoot 프로젝트의 포트 번호를 어떻게 인식합니까? IntellijideAultimate 버전을 사용하여 봄을 시작하십시오 ...

See all articles

핫 AI 도구

Undresser.AI Undress

Undresser.AI Undress

사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover

AI Clothes Remover

사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool

Undress AI Tool

무료로 이미지를 벗다

Clothoff.io

Clothoff.io

AI 옷 제거제

AI Hentai Generator

AI Hentai Generator

AI Hentai를 무료로 생성하십시오.

뜨거운 도구

메모장++7.3.1

메모장++7.3.1

사용하기 쉬운 무료 코드 편집기

SublimeText3 Mac 버전

SublimeText3 Mac 버전

신 수준의 코드 편집 소프트웨어(SublimeText3)

Dreamweaver Mac版

Dreamweaver Mac版

시각적 웹 개발 도구

WebStorm Mac 버전

WebStorm Mac 버전

유용한 JavaScript 개발 도구

스튜디오 13.0.1 보내기

스튜디오 13.0.1 보내기

강력한 PHP 통합 개발 환경