


Android は ViewDragHelper を使用して最新バージョンの QQ6.X サイド スライド インターフェイス効果のサンプル コードを実装します。
(1). 前書き:
QQ がメジャーアップデートされました (6. 今日は、QQ6.X バージョンの横スライド インターフェイス効果を真似して実装してみましょう。現在でも、アーティファクト ViewDragHelper を使用して実装しています
この例の具体的なコードは、以下のプロジェクトにアップロードされています。スターを付けてフォークしてください。
https://github.com/jiangqqlmj/DragHelper4QQ
FastDev4Android フレームワーク プロジェクト アドレス: https://github.com/jiangqqlmj/FastDev4Android
(2).ViewGragHelper の基本的な使い方
以前は、 ViewGragHelper の使用方法と、内部のいくつかのメソッドの目的についても確認してみましょう。 ViewGragHelper を使用してサブ View のドラッグ アンド ドロップ移動を実装する手順は次のとおりです。
ViewGragHelper インスタンスを作成する (Callback に渡す)
イベント インターセプト処理メソッド onInterceptTouch と onTouchEvent を書き換える
Callback を実装する関連するメソッド tryCaptureView と、horizontal またはを実装します。 垂直方向の移動の距離メソッドのより詳細な分析については、以前のブログを読んでいただくか、今日ここで具体的な例を通して説明します。
(3). 左側の関数 View、もう 1 つは上部のメイン関数コンテンツ View です。上部の View をドラッグするか、左右にスライドすると、上下の View がそれに応じてスライドし、View サイズが変化します。が変更され、関連するアニメーションも同時に追加されます。もちろん、上部のビューをクリックして、横スライド メニューを開いたり閉じたりすることもできます。
(4). サイドスライディング効果のカスタムコンポーネントの実装
1. まず、FrameLayoutを統合してカスタムView DragLayoutを作成します。内部的に定義されているいくつかの変数は次のとおりです (主に、いくつかの構成クラス、ジェスチャー、ViewDragHelper インスタンス、画面の幅と高さ、サブビュー View のドラッグなど)
//是否带有阴影效果 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); }
public DragLayout(Context context,AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); gestureDetector = new GestureDetectorCompat(context, new YScrollDetector()); dragHelper =ViewDragHelper.create(this, dragHelperCallback); }
create() メソッドが作成されるときコールバック クラスが渡されます。これについては 4 番目のポイントで説明します。 3. 次に、子 View をドラッグして移動するという目的を達成するために、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; }
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; } }
/** * 设置水平方向滑动的最远距离 *@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(); } }
このメソッドは、サブビューをドラッグ中に指を離したときに呼び出され、左右に移動する意図を決定し、マンビュー(上のビュー)を開閉します。以下は最後に実装されたメソッドです: 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); } };
このメソッドは、サブビューをドラッグして移動すると呼び戻され、移動した座標位置に応じてレフト ビューとメイン ビューを再定義します。同時に、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(); } }
このメソッドにはコード行があります。後でパーセンテージを計算するために使用されます
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的缩放效果,所以我们这边引入了一个动画开源库:
然后根据前面算出来的百分比来缩放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中作如下配置:
具体关于BaseAdapter的使用讲解博客地址如下:
4.正式运行效果如下:
5.因为这边底层需要ViewDragHelper类,所以大家在使用的时候需要导入V4包的,不过我这边直接把ViewGragHelper类的源代码复制到项目中了。
(六).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中文网!

ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

AI Hentai Generator
AIヘンタイを無料で生成します。

人気の記事

ホットツール

メモ帳++7.3.1
使いやすく無料のコードエディター

SublimeText3 中国語版
中国語版、とても使いやすい

ゼンドスタジオ 13.0.1
強力な PHP 統合開発環境

ドリームウィーバー CS6
ビジュアル Web 開発ツール

SublimeText3 Mac版
神レベルのコード編集ソフト(SublimeText3)

ホットトピック











この記事では、2025年の上位4つのJavaScriptフレームワーク(React、Angular、Vue、Svelte)を分析し、パフォーマンス、スケーラビリティ、将来の見通しを比較します。 強力なコミュニティと生態系のためにすべてが支配的なままですが、彼らの相対的なポップ

この記事では、リモートコードの実行を可能にする重大な欠陥であるSnakeyamlのCVE-2022-1471の脆弱性について説明します。 Snakeyaml 1.33以降のSpring Bootアプリケーションをアップグレードする方法は、このリスクを軽減する方法を詳述し、その依存関係のアップデートを強調しています

Javaのクラスロードには、ブートストラップ、拡張機能、およびアプリケーションクラスローダーを備えた階層システムを使用して、クラスの読み込み、リンク、および初期化が含まれます。親の委任モデルは、コアクラスが最初にロードされ、カスタムクラスのLOAに影響を与えることを保証します

この記事では、カフェインとグアバキャッシュを使用してJavaでマルチレベルキャッシュを実装してアプリケーションのパフォーマンスを向上させています。セットアップ、統合、パフォーマンスの利点をカバーし、構成と立ち退きポリシー管理Best Pra

node.js 20は、V8エンジンの改善、特により速いガベージコレクションとI/Oを介してパフォーマンスを大幅に向上させます。 新機能には、より良いWebセンブリのサポートと洗練されたデバッグツール、開発者の生産性とアプリケーション速度の向上が含まれます。

大規模な分析データセットのオープンテーブル形式であるIcebergは、データの湖のパフォーマンスとスケーラビリティを向上させます。 内部メタデータ管理を通じて、寄木細工/ORCの制限に対処し、効率的なスキーマの進化、タイムトラベル、同時wを可能にします

この記事では、Lambda式、Streams API、メソッド参照、およびオプションを使用して、機能プログラミングをJavaに統合することを調べます。 それは、簡潔さと不変性を通じてコードの読みやすさと保守性の改善などの利点を強調しています

この記事では、キュウリの手順間でデータを共有する方法、シナリオコンテキスト、グローバル変数、引数の合格、およびデータ構造を比較する方法を調べます。 簡潔なコンテキストの使用、記述など、保守性のためのベストプラクティスを強調しています
