使用 Android Studio 创建自定义视图

Posted

技术标签:

【中文标题】使用 Android Studio 创建自定义视图【英文标题】:Create a custom view with Android Studio 【发布时间】:2020-08-20 11:01:07 【问题描述】:

我正在尝试在 android Studio 中创建一个可以将其从右向左拖动的小视图。此视图将有 2 个按钮。

当你选择其中一个或按下它的外部时,小菜单将再次隐藏。

我一直在搜索,但没有任何类似的库。我也不知道该怎么做。

我可以在单独的视图(布局 xml)中绘制小视图,但我不知道如何添加它并创建要通过拖动打开或关闭的事件。

我该怎么做?

谢谢。

【问题讨论】:

你尝试NavigationView组件吗? 不完全是,视图更小并且水平包含图标。虽然它的行为相似,因为它是从一侧拖动并且在向外按下时必须关闭 我认为您可以更改navigationView 的大小,然后在其中定义一个relativeLayout,它包含两个button 你试过我的答案了吗? 这是个好主意,我尝试了@Lakindu 的想法。谢谢。 【参考方案1】:

这是创建自定义可拖动抽屉的基本示例。

这些是我看过的参考资料。

为了检测拖动/投掷手势,我使用了 GestureDetectorCompat,并且 我指的是:https://developer.android.com/training/gestures/detector

要创建抽屉打开和关闭动画我提到:https://youtu.be/OHcfs6rStRo

请注意,这是一个非常基本的示例。 您可以以此为基础来创建您的最终目标。 您将不得不过滤掉您收到的不需要的拖拽/甩动回调。 你将不得不忽略你会在抽屉上得到的水龙头。

这是实现。

MainActivity.java

import androidx.appcompat.app.AppCompatActivity;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.constraintlayout.widget.ConstraintSet;
import androidx.core.view.GestureDetectorCompat;

import android.os.Bundle;
import android.transition.TransitionManager;
import android.view.GestureDetector;
import android.view.MotionEvent;

public class MainActivity extends AppCompatActivity 

    private boolean mIsDrawerOpened;
    private ConstraintLayout mRootConstraintLayout;
    private final ConstraintSet mDrawerClosedConstraintSet = new ConstraintSet();
    private final ConstraintSet mDrawerOpenedConstraintSet = new ConstraintSet();
    private GestureDetectorCompat mGestureDetector;

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main_drawer_closed);

        // Drawer is initially closed
        mIsDrawerOpened = false;

        mRootConstraintLayout = findViewById(R.id.rootConstraintLayout);

        mDrawerClosedConstraintSet.clone(this, R.layout.activity_main_drawer_closed);
        mDrawerOpenedConstraintSet.clone(this, R.layout.activity_main_drawer_opened);

        mGestureDetector = new GestureDetectorCompat(
                getApplicationContext(),
                new GestureDetector.SimpleOnGestureListener() 

                    @Override
                    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) 

                        // Drag / Fling gesture detected

                        // TODO: Recongnize unwanted drag / fling gestures and ignore them.

                        TransitionManager.beginDelayedTransition(mRootConstraintLayout);

                        // Drawer is closed?
                        if(!mIsDrawerOpened) 
                            // Open the drawer
                            mDrawerOpenedConstraintSet.applyTo(mRootConstraintLayout);
                            mIsDrawerOpened = true;
                        

                        return true;
                    

                    @Override
                    public boolean onSingleTapUp(MotionEvent e) 

                        // Single tap detected

                        // TODO: If user has tapped on the drawer, do not close it.

                        TransitionManager.beginDelayedTransition(mRootConstraintLayout);

                        // Drawer is opened?
                        if(mIsDrawerOpened) 
                            // Close the drawer
                            mDrawerClosedConstraintSet.applyTo(mRootConstraintLayout);
                            mIsDrawerOpened = false;
                        

                        return true;
                    

                    @Override
                    public boolean onDown(MotionEvent e) 
                        return true;
                    
                
        );
    

    @Override
    public boolean onTouchEvent(MotionEvent event) 
        mGestureDetector.onTouchEvent(event);
        return super.onTouchEvent(event);
    

res/layout/activity_main_drawer_closed.xml

<ConstraintLayout
    android:id="@+id/rootConstraintLayout"
    android:clipChildren="false" >

    <ConstraintLayout
        android:id="@+id/drawerConstraintLayout"
        android:layout_
        android:layout_
        android:background="@color/colorPrimaryDark"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toEndOf="parent" >

        <Button
            android:id="@+id/button1"
            android:layout_
            android:layout_
            android:text="1"
            android:backgroundTint="@color/colorAccent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toStartOf="@id/button2" />

        <Button
            android:id="@+id/button2"
            android:layout_
            android:layout_
            android:text="2"
            android:backgroundTint="@color/colorAccent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toEndOf="@id/button1"
            app:layout_constraintEnd_toEndOf="parent" />

    </ConstraintLayout>

    <ImageView
        android:id="@+id/notch"
        android:layout_
        android:layout_
        android:src="@drawable/drawer_notch"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toStartOf="@id/drawerConstraintLayout" />

</ConstraintLayout>

res/layout/activity_main_drawer_opened.xml

<ConstraintLayout
    android:id="@+id/rootConstraintLayout" >

    <ConstraintLayout
        android:id="@+id/drawerConstraintLayout"
        android:layout_
        android:layout_
        android:background="@color/colorPrimaryDark"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent" >

        <Button
            android:id="@+id/button1"
            android:layout_
            android:layout_
            android:text="1"
            android:backgroundTint="@color/colorAccent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toStartOf="@id/button2" />

        <Button
            android:id="@+id/button2"
            android:layout_
            android:layout_
            android:text="2"
            android:backgroundTint="@color/colorAccent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toEndOf="@id/button1"
            app:layout_constraintEnd_toEndOf="parent" />

    </ConstraintLayout>

    <ImageView
        android:id="@+id/notch"
        android:layout_
        android:layout_
        android:src="@drawable/drawer_notch"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toStartOf="@id/drawerConstraintLayout" />

</ConstraintLayout>

res/drawable/drawer_notch.xml

<shape
    android:shape="rectangle">
    <corners android:radius="4dp" />
    <solid android:color="@color/colorAccent" />
</shape>

app/build.gradle

android 
    defaultConfig 
        minSdkVersion 19
        .
        .
        .
    
    .
    .
    .

结果:

【讨论】:

非常好,它对我来说工作正常。您只需指出必须将以下内容添加到 Main_Activity.xml:&lt;include layout="@layout/activity_main_drawer_closed" /&gt;。谢谢@Lakindu 我很高兴它对你有用。在此示例中,用于MainActivity 的布局资源为activity_main_drawer_closed.xml。您可以在onCreate 方法中看到它。这里没有main_activity.xml。是的,如果你愿意,你可以有一个main_activity.xml 并在其中包含activity_main_drawer_closed 布局。 嗨@Lakindu,我在使用旧的 Android 版本时遇到了问题(例如,版本 21)。抽屉打开布局不显示,只有缺口。你能模仿它看看发生了什么吗?谢谢。 @KrmX,是的,你是对的。当我在 Android L 模拟器上运行它时,我可以看到这一点。 TransitionManager API 在 API 级别 14 中可用。因此,动画也应该在级别 21 中工作。我会调查并尝试为您提供解决方案。 @KrmX,您可以找到解决方案here 来解决问题。我在 API 级别 29 和 19 上对其进行了测试,并在两个平台上都获得了预期的行为。我编辑此答案以包含该修复程序。【参考方案2】:

首先在你的Gradle 中添加android material 依赖:

implementation  'com.google.android.material:material:1.1.0'

然后你可以像这样使用NavigationView 组件:

<com.google.android.material.navigation.NavigationView
    android:id="@+id/navi_view"
    android:layout_
    android:layout_
    android:background="@color/colorPrimary"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintTop_toTopOf="parent">

    <Button
        android:id="@+id/btn1"
        android:layout_
        android:layout_
        android:text="btn1" />

    <Button
        android:id="@+id/btn2"
        android:layout_
        android:layout_
        android:layout_gravity="end"
        android:text="btn2" />


</com.google.android.material.navigation.NavigationView>

这是一个非常简单的例子,没有约定,你应该改进它。

【讨论】:

以上是关于使用 Android Studio 创建自定义视图的主要内容,如果未能解决你的问题,请参考以下文章

如何正确连接并显示从 Firebase 到自定义列表视图 Android Studio 的数据

Android Studio自定义加固插件

Android Studio,如何使用数组或光标中的数据填充自定义 ListView?

使用Android studio创建的AIDL编译时找不到自定义类的解决办法

在 Android Studio 中向列表视图添加自定义行

在 android studio 中创建自定义小部件?