版本:1.0
日期:2014.4.29
版权:© 2014 kince 转载注明出处
关于View的拖动大家应该比较了解了,比如对一个控件IamgeView拖动,或者一个视图View拖动,实现方式也很容易,继承OnTouchListener接口,然后重写onTouch方法,在触屏事件进行处理即可。但是Popupwindow如何实现拖动呢,我们都知道它和普通的View不一样,因为它不是继承于View类的,但是它的实现却是和View密切相关的,因为我们都知道Android视图的显示都是由View来处理的,所以一定离不开它。从Popupwindow的实现就可以看出来,
import com.android.internal.R; import android.content.Context; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.graphics.drawable.StateListDrawable; import android.os.Build; import android.os.IBinder; import android.util.AttributeSet; import android.view.Gravity; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; import android.view.View.OnTouchListener; import android.view.ViewGroup; import android.view.ViewTreeObserver; import android.view.ViewTreeObserver.OnScrollChangedListener; import android.view.WindowManager; import java.lang.ref.WeakReference;上面是它的导包情况,基本上不是和View相关,就是和绘图相关。因此关于Popupwindow的拖动这一块,也和View有联系。首先看一下它的API,看一看有没有和View移动、变化相关的方法,果然在最后有几个update()方法,如下:
update()方法用来更新Popupwindow的位置和大小的,那么问题就好解决了。看代码:
package com.example.drag_and_drop_movablepopupwindow; import android.support.v7.app.ActionBarActivity; import android.graphics.Color; import android.os.Bundle; import android.view.Gravity; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; import android.view.View.OnTouchListener; import android.view.ViewGroup.LayoutParams; import android.widget.Button; import android.widget.PopupWindow; import android.widget.TextView; public class MainActivity extends ActionBarActivity { private Button btnOpenPopup; private int mCurrentX; private int mCurrentY; private PopupWindow mPopup; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btnOpenPopup = (Button) findViewById(R.id.openpopup); btnOpenPopup.setOnClickListener(new Button.OnClickListener() { @Override public void onClick(View arg0) { creatPopubWindow_1(); } }); } /** * 1 */ private void creatPopubWindow_1() { LayoutInflater layoutInflater = (LayoutInflater) getBaseContext() .getSystemService(LAYOUT_INFLATER_SERVICE); View popupView = layoutInflater.inflate(R.layout.popup, null); final PopupWindow popupWindow = new PopupWindow(popupView, 200, 200); Button btnDismiss = (Button) popupView.findViewById(R.id.dismiss); btnDismiss.setOnClickListener(new Button.OnClickListener() { @Override public void onClick(View v) { popupWindow.dismiss(); } }); popupWindow.showAsDropDown(btnOpenPopup, 50, 50); popupView.setOnTouchListener(new OnTouchListener() { int orgX, orgY; int offsetX, offsetY; @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: orgX = (int) event.getX(); orgY = (int) event.getY(); break; case MotionEvent.ACTION_MOVE: offsetX = (int) event.getRawX() - orgX; offsetY = (int) event.getRawY() - orgY; popupWindow.update(offsetX, offsetY, -1, -1, true); break; } return true; } }); } }
效果如图:
首先对Popupwindow设置触摸事件,然后在回调方法中进行计算,如果手指拖动了Popupwindow,那么就调用update()方法来更新它的位置。有些同学可能不太理解参数-1是什么意思,在上面的API中,写明的是宽和高,这里怎么变成-1了呢,看一下Popupwindow源代码就明白了。
/** * <p>Updates the position and the dimension of the popup window. Width and * height can be set to -1 to update location only. Calling this function * also updates the window with the current popup state as * described for {@link #update()}.</p> * * @param x the new x location * @param y the new y location * @param width the new width, can be -1 to ignore * @param height the new height, can be -1 to ignore * @param force reposition the window even if the specified position * already seems to correspond to the LayoutParams */ public void update(int x, int y, int width, int height, boolean force) { if (width != -1) { mLastWidth = width; setWidth(width); } if (height != -1) { mLastHeight = height; setHeight(height); } if (!isShowing() || mContentView == null) { return; } WindowManager.LayoutParams p = (WindowManager.LayoutParams) mPopupView.getLayoutParams(); boolean update = force; final int finalWidth = mWidthMode < 0 ? mWidthMode : mLastWidth; if (width != -1 && p.width != finalWidth) { p.width = mLastWidth = finalWidth; update = true; } final int finalHeight = mHeightMode < 0 ? mHeightMode : mLastHeight; if (height != -1 && p.height != finalHeight) { p.height = mLastHeight = finalHeight; update = true; } if (p.x != x) { p.x = x; update = true; } if (p.y != y) { p.y = y; update = true; } final int newAnim = computeAnimationResource(); if (newAnim != p.windowAnimations) { p.windowAnimations = newAnim; update = true; } final int newFlags = computeFlags(p.flags); if (newFlags != p.flags) { p.flags = newFlags; update = true; } if (update) { setLayoutDirectionFromAnchor(); mWindowManager.updateViewLayout(mPopupView, p); } }前两个if判断已经说得很清楚了,如果参数是-1的话,就不改变Popupwindow的大小了,因为我们只是移动位置,所以才这样写。那关于Popupwindow的移动最后是怎么实现的呢,可以看出就是调用WindowManager的updateViewLayout()方法,这个方法在WindowManager中并没有实现,它是ViewManager接口里面的方法,WindowManager继承了ViewManager。说到ViewManager,它里面定义的方法都很常用,看代码:
/** Interface to let you add and remove child views to an Activity. To get an instance * of this class, call {@link android.content.Context#getSystemService(java.lang.String) Context.getSystemService()}. */ public interface ViewManager { /** * Assign the passed LayoutParams to the passed View and add the view to the window. * <p>Throws {@link android.view.WindowManager.BadTokenException} for certain programming * errors, such as adding a second view to a window without removing the first view. * <p>Throws {@link android.view.WindowManager.InvalidDisplayException} if the window is on a * secondary {@link Display} and the specified display can't be found * (see {@link android.app.Presentation}). * @param view The view to be added to this window. * @param params The LayoutParams to assign to view. */ public void addView(View view, ViewGroup.LayoutParams params); public void updateViewLayout(View view, ViewGroup.LayoutParams params); public void removeView(View view); }这下大家应该明了,我们经常用的addView、removeView方法就是在这里面定义的,那么谁去实现呢?就是Layout控件,比如LinearLayout、RelativeLayout等,所以我们刚才用的updateViewLayout()方法也是在xml布局文件中的layout定义好的。