QQ は便利で実用的なチャットツールです。QQ が 6.0 にアップデートされてから、サイドのスライドが元の状態から減り、左右に滑らかになりました。大幅に改善されているので、内部のさまざまなロジックを理解し、関連する情報を組み合わせて研究するだけです。
ここでの主要なクラスの 1 つが ViewDragHelper であることはわかっているので、まずこの ViewDragHelper クラスを理解する必要があります。よく言われるように、体長 7 インチになるまで蛇を倒すのは難しいです。まず公式の仕組みを見てみましょう。このドキュメントでは、それがどのようなユニークな機能を持っているかを紹介しています。
最初の継承:
java.lang.Object
android.support.v4.widget.ViewDragHelper
直接の親クラスはObjectです。
クラスの概要
ViewDragHelper は、カスタム ViewGroup を作成するためのユーティリティ クラスであり、ユーザーが親 ViewGroup 内でビューをドラッグしたり位置変更したりできるようにするための、多数の便利な操作と状態追跡を提供します。 ViewGroups。ユーティリティ クラス。このクラスは、ユーザーが親 ViewGroup 内で軌跡と位置をドラッグして描画できるようにする多くの便利なメソッドと状態を提供します。
ネストされたクラス (ネストされたクラス)
ViewDragHelper.Callback
コールバックは、ViewDragHelper を使用して親ビューに戻る通信チャネルとして使用されます。
コールバックは、ViewDragHelper を使用して親ビューに戻る通信チャネルとして使用されます。それを使用する親ビュー。 通信インターフェイス
パブリック静的メソッド:
ViewDragHelper が create() メソッドを通じて構築されることがわかります。これについては後ほど詳しく紹介します。
使用する必要があるメソッドのいくつかを見てみましょう:
public boolean tryCaptureView(View child, int pointerId) {} public int getViewHorizontalDragRange(View child) {} public int clampViewPositionHorizontal(View child, int left, int dx) {} public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {} public void onViewReleased(View releasedChild, float xvel, float yvel) {}
上記のメソッドには、コード内に詳細なコメントが含まれています。ここでは、書き換える必要があるメソッドだけを見てみましょう
さて、ここまでは述べました。 、ドラッグ アンド ドロップできる簡単なビューから始めましょう (2 つのビューをドラッグ アンド ドロップできるようになると、ほんの数ステップで非常に簡単であることがわかると思います):
最初のステップは次のことを認識することです。ドラッグ アンド ドロップ機能 (3 つの簡単な手順で実装)
//1、通过静态方法初始化操作 mDragHelper = ViewDragHelper.create(this, mCallback); /** * 2、传递触摸事件 * * @param ev * @return */ @Override public boolean onInterceptTouchEvent(MotionEvent ev) { //让自己的控件自行判断是否去拦截 return mDragHelper.shouldInterceptTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent event) { try { //自己去处理触摸事件 mDragHelper.processTouchEvent(event); } catch (Exception e) { e.printStackTrace(); } //返回true,这样才能持续接收,要不然我们不会传递而是被拦截了 return true; } ViewDragHelper.Callback mCallback = new ViewDragHelper.Callback() { /** * 根据返回结果决定当前child是否可以拖拽 * 尝试捕获的时候调用,如果返回的是主面板,那么负面版是不能被调用的 * @param child 当前被拖拽的view * @param pointerId 区分多点触摸的id * @return 返回true 是都可以拖拽 * 返回child == mLeftContent 左侧可以移动 * 返回child == mMainContent 右侧可以移动 */ @Override public boolean tryCaptureView(View child, int pointerId) { //这里要返回true,要不然不能拖拽 return true; } /** * 根据建议值修正将要移动的位置 此时并没有发生真正的移动(左右) * * @param child 当前拖拽的view * @param left 新的位置的建议值 oldLeft + dx * @param dx 变化量 和变化之前位置的差值 * @return */ @Override public int clampViewPositionHorizontal(View child, int left, int dx) { //返回的拖拽的范围,不对拖拽进行真正的限制,仅仅决定了动画执行的速度 return left; } };
さて、最初のレンダリングが完了しました。つまり、ドラッグ アンド ドロップで実現できます。
しかし、送信した場合。このようなタスク、もう作業したくないですか? さて、彼がランダムにドラッグできないようにドラッグ位置を制御しましょう。 ,
2 番目のステップ、ドラッグ範囲の制御です
ドラッグ範囲を制御したいのですが、操作する前に、まずこれら 2 つのコントロールを取得し、これら 2 つのコントロールのプロパティを取得する必要があります。そこで、メソッドを書き直しました:
/** * Finalize inflating a view from XML. This is called as the last phase * of inflation, after all child views have been added. * 当xml填充完的时候去掉用,在这里我们可以找到我们要去操控的那两个布局 * <p>Even if the subclass overrides onFinishInflate, they should always be * sure to call the super method, so that we get called. */ @Override protected void onFinishInflate() { super.onFinishInflate(); /** * 根据索引来找 */ /** * 得到左边的布局 */ mLeftContent = (ViewGroup) getChildAt(0); /** * 得到主main布局 */ mMainContent = (ViewGroup) getChildAt(1); }
次に、幅と高さを取得する必要があります。onMessure() で取得できることはわかっていますが、確認すると、次のメソッドも取得できます。
/** * 当尺寸变化的时候去调用 * This is called during layout when the size of this view has changed * @param w * @param h * @param oldw * @param oldh */ @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); /** * 屏幕的宽度和高度 */ mHeight = getMeasuredHeight(); mWidth = getMeasuredWidth(); /** * 自定义左侧view拖拽出来的距离 */ mRange = (int) (mWidth * 0.7); }
必要な幅、高さ、ドラッグ距離が得られたら、次にそれを制限します。
これをクランプViewPositionhorizontal() メソッドに追加するだけで、メイン パネルが左側に移動するのを制限でき、左側のパネルは右側の mRange 位置にのみ移動できます。 (ここで試すことができます)
if (child == mMainContent) { left = fixLeft(left); } /** * 修正方法 * 根据范围去修正左侧的view的可见 * * @param left * @return */ private int fixLeft(int left) { if (left < 0) { return 0; } else if (left > mRange) { return mRange; } return left; }
現時点で、基本的な一般的なフレームワークは出てきましたが、まだ問題があります。つまり、スライド効果を達成しましたが(右側のものは固定されていますが、いつLeftView をスライドさせても、右にスライドできます) それを制限する方法を見つける必要があります。つまり、制限するには、左側のビューを mRange 距離だけ右にスライドすることしかできません。次に、どのメソッドが変更を取得できるかを確認し、それを変更する必要があります。
/** * 当view位置改变的时候,处理要做的事情,更新状态,伴随动画,重绘界面 * * 此时view已经发生了位置的改变 * * @param changedView 改变的位置view * @param left 新的左边值 * @param top * @param dx 水平变化量 * @param dy */ @Override public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) { super.onViewPositionChanged(changedView, left, top, dx, dy); int newLeft = left; //如果我们拖拽的是左面板 if (changedView == mLeftContent) { //新的左侧位置是我们的主面板的左侧加上水平变化量 newLeft = mMainContent.getLeft() + dx; } //进行修正(不能超出我们的规定的范围) newLeft = fixLeft(newLeft); if (changedView == mLeftContent) { //当左面板移动之后,在强制放回去 mLeftContent.layout(0, 0, 0 + mWidth, 0 + mHeight); mMainContent.layout(newLeft, 0, newLeft + mWidth, 0 + mHeight); } //兼容低版本 强制重绘 invalidate(); }
さて、この時点で全体的な効果が得られました。レンダリングを見てください。 (ここで真ん中の白い色は記録の問題であり、私の個人的なテストでは問題ありませんでした)
この時点で、大まかにドラッグアンドドロップが実現できます。 もちろん、現在の私のレイアウトは以下の簡単な実装です。最初にコントロールを追加する必要があります。これを 2 つの LinearLayout に自分で追加できます)
<?xml version="1.0" encoding="utf-8"?> <com.example.qqsliding.DragLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.example.qqsliding.MainActivity"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:background="@mipmap/sidebar_bg"> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:background="#FFFFFF"> <RelativeLayout android:layout_width="match_parent" android:layout_height="50dp" android:background="#0CB8F6" android:gravity="center_vertical"> <ImageView android:layout_width="30dp" android:layout_height="30dp" android:layout_marginLeft="15dp" android:src="@mipmap/icon_avatar_white"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:text="Header"/> <ImageView android:layout_width="30dp" android:layout_height="30dp" android:layout_alignParentRight="true" android:layout_marginRight="15dp" android:src="@mipmap/icon_search"/> </RelativeLayout> </LinearLayout> </com.example.qqsliding.DragLayout>
しかし、注意している人は、ドラッグすると特に硬いことに気づくかもしれません。これは、アニメーション効果を追加せず、単にドラッグしたためです。具体的にアニメーションを追加し、コールバック効果を定義するには、この記事を読んで Android のスライディング最適化の高模倣 QQ6.0 サイド スライディング メニュー (2) を読んでください。
ViewDragHelper を使用して QQ6.0 のようなサイド スライド インターフェイスを実装する Android の詳細 (1) については、PHP 中国語 Web サイトの関連記事に注目してください。