Facebook最新的Android应用程序有一个非常好的浮动评论窗口,用户可以向上或向下滑动,轻松地将其关闭,使其非常易于使用。
我想在我的应用程序中实现类似的行为,但我不知道如何做。如果您有任何想法或线索,请非常感谢。
Facebook应用程序截图
(对不起,我截屏的Facebook应用程序是日文版)
Facebook最新的Android应用程序有一个非常好的浮动评论窗口,用户可以向上或向下滑动,轻松地将其关闭,使其非常易于使用。
我想在我的应用程序中实现类似的行为,但我不知道如何做。如果您有任何想法或线索,请非常感谢。
Facebook应用程序截图
(对不起,我截屏的Facebook应用程序是日文版)
Transluscent
主题,以获取具有透明背景的活动。
在我的manifest.xml文件中:
<activity
android:name=".PopupActivity"
android:label="@string/title_activity_popup"
<!-- Use Translucent theme to get transparent activity background
and NoTitleBar to avoid super old style title bar ;) -->
android:theme="@android:style/Theme.Translucent.NoTitleBar">
</activity>
然后我创建一个简单的布局文件,其中包含一个TextView(对应Facebook聊天部分)和一个View(对应Facebook“编写消息”/“发送表情符号”选项卡)。
我的layout/activity_popup.xml文件:
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/base_popup_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="8dp"
android:background="@android:color/darker_gray"
android:layout_marginBottom="124dp">
<TextView
android:text="@string/hello_world"
android:layout_width="match_parent"
android:layout_height="200dp"
android:background="@android:color/black"/>
<View
android:layout_width="match_parent"
android:layout_height="80dp"
android:layout_alignParentBottom="true"
android:background="@android:color/holo_blue_dark"/>
</RelativeLayout>
最终,我在我的PopupActivity类中处理了触摸和移动事件,我使用OnTouchListener,它提供了在onTouch方法中的回调。
PopupActivity
public class PopupActivity extends Activity implements View.OnTouchListener{
private RelativeLayout baseLayout;
private int previousFingerPosition = 0;
private int baseLayoutPosition = 0;
private int defaultViewHeight;
private boolean isClosing = false;
private boolean isScrollingUp = false;
private boolean isScrollingDown = false;
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_popup);
baseLayout = (RelativeLayout) findViewById(R.id.base_popup_layout);
baseLayout.setOnTouchListener(this);
}
public boolean onTouch(View view, MotionEvent event) {
// Get finger position on screen
final int Y = (int) event.getRawY();
// Switch on motion event type
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
// save default base layout height
defaultViewHeight = baseLayout.getHeight();
// Init finger and view position
previousFingerPosition = Y;
baseLayoutPosition = (int) baseLayout.getY();
break;
case MotionEvent.ACTION_UP:
// If user was doing a scroll up
if(isScrollingUp){
// Reset baselayout position
baseLayout.setY(0);
// We are not in scrolling up mode anymore
isScrollingUp = false;
}
// If user was doing a scroll down
if(isScrollingDown){
// Reset baselayout position
baseLayout.setY(0);
// Reset base layout size
baseLayout.getLayoutParams().height = defaultViewHeight;
baseLayout.requestLayout();
// We are not in scrolling down mode anymore
isScrollingDown = false;
}
break;
case MotionEvent.ACTION_MOVE:
if(!isClosing){
int currentYPosition = (int) baseLayout.getY();
// If we scroll up
if(previousFingerPosition >Y){
// First time android rise an event for "up" move
if(!isScrollingUp){
isScrollingUp = true;
}
// Has user scroll down before -> view is smaller than it's default size -> resize it instead of change it position
if(baseLayout.getHeight()<defaultViewHeight){
baseLayout.getLayoutParams().height = baseLayout.getHeight() - (Y - previousFingerPosition);
baseLayout.requestLayout();
}
else {
// Has user scroll enough to "auto close" popup ?
if ((baseLayoutPosition - currentYPosition) > defaultViewHeight / 4) {
closeUpAndDismissDialog(currentYPosition);
return true;
}
//
}
baseLayout.setY(baseLayout.getY() + (Y - previousFingerPosition));
}
// If we scroll down
else{
// First time android rise an event for "down" move
if(!isScrollingDown){
isScrollingDown = true;
}
// Has user scroll enough to "auto close" popup ?
if (Math.abs(baseLayoutPosition - currentYPosition) > defaultViewHeight / 2)
{
closeDownAndDismissDialog(currentYPosition);
return true;
}
// Change base layout size and position (must change position because view anchor is top left corner)
baseLayout.setY(baseLayout.getY() + (Y - previousFingerPosition));
baseLayout.getLayoutParams().height = baseLayout.getHeight() - (Y - previousFingerPosition);
baseLayout.requestLayout();
}
// Update position
previousFingerPosition = Y;
}
break;
}
return true;
}
}
当用户滚动足够关闭弹出窗口时,有两种小方法被调用(即动画和完成活动):
public void closeUpAndDismissDialog(int currentPosition){
isClosing = true;
ObjectAnimator positionAnimator = ObjectAnimator.ofFloat(baseLayout, "y", currentPosition, -baseLayout.getHeight());
positionAnimator.setDuration(300);
positionAnimator.addListener(new Animator.AnimatorListener()
{
. . .
@Override
public void onAnimationEnd(Animator animator)
{
finish();
}
. . .
});
positionAnimator.start();
}
public void closeDownAndDismissDialog(int currentPosition){
isClosing = true;
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
int screenHeight = size.y;
ObjectAnimator positionAnimator = ObjectAnimator.ofFloat(baseLayout, "y", currentPosition, screenHeight+baseLayout.getHeight());
positionAnimator.setDuration(300);
positionAnimator.addListener(new Animator.AnimatorListener()
{
. . .
@Override
public void onAnimationEnd(Animator animator)
{
finish();
}
. . .
});
positionAnimator.start();
}
有了这些代码,您应该能够启动类似Facebook弹出窗口的PopupActivity。这只是一个草稿类,还有很多工作要做:添加动画,处理关闭参数等等...
屏幕截图:
因此,这将通过 DialogFragment
实现,它提供了自动弹回对话框到其原始状态/窗口大小的行为,无论何时您向上或向下滑动对话框。当对话框滑动一点(确切地说是滑动距离小于原始布局大小的一半)时,这种行为将被保留。
剩下的事情是如果对话框的大小小于原始窗口大小的一半,则解除此对话框;换句话说,如果它的滑动距离超过原始布局大小的一半。这部分调整自接受的答案,只更改对话窗口根布局的Y
位置,而不更改窗口的大小,因为这会提供奇怪的调整大小行为。
首先创建此样式以使对话框窗口具有透明背景:
<style name="NoBackgroundDialogTheme" parent="Theme.AppCompat.Light.Dialog">
<item name="android:windowBackground">@null</item>
</style>
通过重写getTheme()
方法,可以在DialogFragment中应用此样式。
以下是定制的DialogFragment:
class MyDialogFragment : DialogFragment(), View.OnTouchListener {
private var rootLayoutY: Int = 0
private val rootLayout by lazy {
requireView().findViewById<ConstraintLayout>(R.id.dialog_root)
}
private var oldY = 0
private var baseLayoutPosition = 0
private var defaultViewHeight = 0
private var isScrollingUp = false
private var isScrollingDown = false
override fun getTheme(): Int {
return R.style.NoBackgroundDialogTheme
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
val view: View = inflater.inflate(
R.layout.fragment_dialog_facebook_comment, container,
false
)
view.setBackgroundResource(R.drawable.rounded_background)
return view
}
override fun onStart() {
super.onStart()
// Making the dialog full screen
dialog?.window?.setLayout(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.MATCH_PARENT
)
}
@SuppressLint("ClickableViewAccessibility")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
rootLayout.setOnTouchListener(this)
rootLayout.viewTreeObserver.addOnGlobalLayoutListener(object :
OnGlobalLayoutListener {
override fun onGlobalLayout() {
rootLayout.viewTreeObserver
.removeOnGlobalLayoutListener(this)
// save default base layout height
defaultViewHeight = rootLayout.height
}
})
}
@SuppressLint("ClickableViewAccessibility")
override fun onTouch(v: View?, event: MotionEvent?): Boolean {
// Get finger position on screen
val y = event!!.rawY.toInt()
// Switch on motion event type
when (event.action and MotionEvent.ACTION_MASK) {
MotionEvent.ACTION_DOWN -> {
// Init finger and view position
oldY = y
baseLayoutPosition = rootLayout.y.toInt()
}
MotionEvent.ACTION_UP -> {
if (rootLayoutY >= defaultViewHeight / 2) {
dismiss()
return true
}
// If user was doing a scroll up
if (isScrollingUp) {
// Reset baselayout position
rootLayout.y = 0f
// We are not in scrolling up mode anymore
isScrollingUp = false
}
// If user was doing a scroll down
if (isScrollingDown) {
// Reset baselayout position
rootLayout.y = 0f
// Reset base layout size
rootLayout.layoutParams.height = defaultViewHeight
rootLayout.requestLayout()
// We are not in scrolling down mode anymore
isScrollingDown = false
}
}
MotionEvent.ACTION_MOVE -> {
rootLayoutY = abs(rootLayout.y.toInt())
// Change base layout size and position (must change position because view anchor is top left corner)
rootLayout.y = rootLayout.y + (y - oldY)
if (oldY > y) { // scrolling up
if (!isScrollingUp) isScrollingUp = true
} else { // Scrolling down
if (!isScrollingDown) isScrollingDown = true
}
// Update y position
oldY = y
}
}
return true
}
}
ConstraintLayout
。