在安卓系统中创建倾斜视图

6

我正在寻找一种在Android中制作某种倾斜视图的方法(ImageView,Button或Frame),可以在其上实现onclick。

Windows Phone中有一些类似于这样的样式:

enter image description here

或者像这样:

enter image description here

屏幕的每个部分(黑线上方还是下方)都是可点击的,会出现新的活动。我使用staggeredgridview可以展示不同列大小的网格,但是我想我应该制作自定义布局框架而不是网格来实现这种方法?

我在play.google.com上找到了另一个此类示例:

enter image description here

它是一种制作封面的应用,在某个地方可以更改图片,但没有对每个视图进行点击的功能。

编辑:来自Ernir的部署建议:

public abstract class MainActivity extends Activity implements View.OnTouchListener {
final ImageView iv= (ImageView)findViewById(R.id.img_cut_1);
final ImageView iv2= (ImageView)findViewById(R.id.img_cut_2); 

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    findViewById(R.id.img_cut_1).setOnTouchListener(new MyTouchListener());

}

private final class MyTouchListener implements OnTouchListener {
    @Override
    public boolean onTouch(View v, MotionEvent me) {
        if (me.getAction() == MotionEvent.ACTION_DOWN) {
            int vid = v.getId();
            int nTouchX = (int)me.getX();
            int nTouchY = (int)me.getY();
            if (vid == R.id.img_cut_1) {
                Bitmap bm = ((BitmapDrawable)iv.getDrawable()).getBitmap();
                if (bm.getPixel(nTouchX, nTouchY) != 0) {
                   Log.i("MyActivity", "img_cut_1 true");
                    return true;
                }
                return false;
            }
            if (vid == R.id.img_cut_2) {
                Bitmap bm = ((BitmapDrawable)iv2.getDrawable()).getBitmap();
                if (bm.getPixel(nTouchX, nTouchY) != 0) {
                    Log.i("MyActivity", "img_cut_2 true");
                    return true;
                }

                return false;
            }
        }
        return true;
    }
    } 

布局:

<RelativeLayout 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"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >

        <FrameLayout
            android:id="@+id/frame"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" >

        <ImageView
            android:id="@+id/img_cut_1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:scaleType="center"
            android:src="@drawable/image_cut_1" />
        <ImageView
            android:id="@+id/img_cut_2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:scaleType="center"
            android:src="@drawable/ic_launcher" />
    </FrameLayout>
    </RelativeLayout>

但是遇到了这个错误:
01-06 11:06:33.242: E/AndroidRuntime(15737): FATAL EXCEPTION: main
01-06 11:06:33.242: E/AndroidRuntime(15737): java.lang.RuntimeException: Unable to instantiate activity ComponentInfo{com.example.test.com/com.example.test.com.MainActivity}: java.lang.InstantiationException: com.example.test.com.MainActivity
01-06 11:06:33.242: E/AndroidRuntime(15737):    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1573)
01-06 11:06:33.242: E/AndroidRuntime(15737):    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1667)
01-06 11:06:33.242: E/AndroidRuntime(15737):    at android.app.ActivityThread.access$1500(ActivityThread.java:117)
01-06 11:06:33.242: E/AndroidRuntime(15737):    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:935)
01-06 11:06:33.242: E/AndroidRuntime(15737):    at android.os.Handler.dispatchMessage(Handler.java:99)
01-06 11:06:33.242: E/AndroidRuntime(15737):    at android.os.Looper.loop(Looper.java:130)
01-06 11:06:33.242: E/AndroidRuntime(15737):    at android.app.ActivityThread.main(ActivityThread.java:3687)
01-06 11:06:33.242: E/AndroidRuntime(15737):    at java.lang.reflect.Method.invokeNative(Native Method)
01-06 11:06:33.242: E/AndroidRuntime(15737):    at java.lang.reflect.Method.invoke(Method.java:507)
01-06 11:06:33.242: E/AndroidRuntime(15737):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:867)
01-06 11:06:33.242: E/AndroidRuntime(15737):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:625)
01-06 11:06:33.242: E/AndroidRuntime(15737):    at dalvik.system.NativeStart.main(Native Method)
01-06 11:06:33.242: E/AndroidRuntime(15737): Caused by: java.lang.InstantiationException: com.example.test.com.MainActivity
01-06 11:06:33.242: E/AndroidRuntime(15737):    at java.lang.Class.newInstanceImpl(Native Method)
01-06 11:06:33.242: E/AndroidRuntime(15737):    at java.lang.Class.newInstance(Class.java:1409)
01-06 11:06:33.242: E/AndroidRuntime(15737):    at android.app.Instrumentation.newActivity(Instrumentation.java:1021)
01-06 11:06:33.242: E/AndroidRuntime(15737):    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1565)
01-06 11:06:33.242: E/AndroidRuntime(15737):    ... 11 more

我尝试将整个方法添加到另一个活动中,但没有帮助。 因为您提到“相同的OnTouchListener适用于所有ImageViews”,所以我将该方法添加到类中。


1
InstantiationException 表示 Android 无法实例化您的类。从类名中删除 abstract,将其更改为 "public class MainActivity"。 - Ernir Erlingsson
我已经尝试过了,它给出了以下信息: MainActivity 类必须实现继承的抽象方法 View.OnTouchListener.onTouch(View, MotionEvent)。 - Ahad Porkar
非常感谢!我按照您的建议将类放入我的Oncreate中,这使它正常工作了!11小时后,奖励归您所有 :) 由于Stackoverflow消息“您可以在11小时内授予您的赏金。” 谢谢! - Ahad Porkar
1
是的,这很正常,你正在实现View.OnTouchListener接口... Andrey,在你承担这样的任务之前,你真的需要花更多的时间学习基础知识。将onTouch(..)方法完全移动到MainActivity类中,并删除不必要的MyTouchListener类。 - Ernir Erlingsson
谢谢,我已经做了,是我的错误。 - Ahad Porkar
不用谢,Andrey。祝你未来的所有努力好运;) - Ernir Erlingsson
3个回答

4
最好的解决方案是将规范化图像分成多边形,其中每个多边形表示一个可点击区域。然后,您附加一个OnTouchListener并检查规范化触摸坐标是否在多边形内。这种解决方案需要程序员进行一些工作以及实现几何算法。我不会深入介绍此解决方案,而是向您提供最简单的解决方案,该解决方案需要您花费最少的时间来实现。我猜这就是您想要的,希望我没猜错 :)

没有第三方库的最简单解决方案

请注意,此解决方案不具有内存效率,并应谨慎使用

1. 您将图像切成多个相同大小的图像,其中每个图像仅包含单个可点击区域,其余部分为透明。如果我以您问题中的第一张图片为例,则其中一个这些图像将如下所示:

sample
(source: magma.is)

2. 现在您需要在多个重叠的ImageView中叠加这些图像

....
<FrameLayout
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" >

    <ImageView
        android:id="@+id/img_cut_1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:scaleType="center"
        android:src="@drawable/image_cut_1" />
    <ImageView
        android:id="@+id/img_cut_2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:scaleType="center"
        android:src="@drawable/image_cut_2" />
...

3. 在您的Activity/Fragment中,您将相同的 OnTouchListener 设置给所有的ImageView。通过透明度消除,您可以发现哪个ImageView真正被触摸,然后您就可以获得关于图像(或按钮)哪个区域被按下的信息。

@Override
public boolean onTouch(View v, MotionEvent me) {
    if (me.getAction() == MotionEvent.ACTION_DOWN) {
        int nTouchX = (int)me.getX();
        int nTouchY = (int)me.getY();
        if (v == imageCut1) {
            Bitmap bm = ((BitmapDrawable)imageCut1.getDrawable()).getBitmap();
            if (bm.getPixel(nTouchX, nTouchY) != 0) {
                // non-transparent pixel touched, we found our view, receive further motion events. This can also be set false and code inserted here.
                return true;
            }
            // transparent pixel touched, do not receive further motion events.
            return false;
        }
        if (v == imageCut2) {
            ...

如果您只想要用户轻触,则可以继续并行使用 OnClick,或者更好地使用 MotionEvent.ACTION_UP 动作,并检查 DOWN 和 UP 运动事件之间的轻触漂移和最大时间延迟。在 MotionEvent.ACTION_UP 中添加以下代码(请注意,只有“正确”的 ImageView 会给您此运动事件,因为我们已经丢弃了其他视图的运动事件)。
if (
    mMainDragStartX <(me.getX()+TAP_DRIFT_TOLERANCE) && 
    mMainDragStartX > (me.getX() -TAP_DRIFT_TOLERANCE) && 
    mMainDragStartY <  (me.getY() + TAP_DRIFT_TOLERANCE) &&
    mMainDragStartY > (me.getY() - TAP_DRIFT_TOLERANCE) &&
    ((SystemClock.elapsedRealtime() - mMainDraggingStarted) < SINGLE_TAP_MAX_TIME)) {
// The user has tapped!
...

谢谢回答,我已经尝试了上面的代码,但是不幸的是,每次我点击任何地方时,这条消息都会出现在logcat中,什么也没有发生: I/InputReader(1500): dispatchTouch::touch event's action is 1 I/InputDispatcher(1500): Delivering touch to current input target 您能否详细说明或解释一下这段代码。 - Ahad Porkar
logcat中的那个消息仅供参考,它并没有真正提供任何关于你遇到的问题的提示。我现在不知道该如何更好地解释了。我建议将过程分解为较小的步骤,尝试从单个ImageView接收ACTION_DOWN事件,然后再从那里开始。顺便说一下:我已经在一个发布的应用程序中成功使用了这种方法。 - Ernir Erlingsson
我尝试了您的方法,但出现了错误,并已添加到问题中。您能否提供更多帮助并检查一下? - Ahad Porkar

1

你需要自定义布局,让我举一个例子

QuiltViewLibrary

StaggeredGridView

StaggeredGridView 更像 Pinterest 的布局。因此,您将使用其中之一作为模板,并编写一个新的 GridLayout 类,将图像放在更改为梯形或三角形的位置。

不,它不是非网格布局...你必须自定义网格布局。

平铺数学将有些像彭罗斯,搜索一下谷歌,您将找到用于此目的的 Java 库。


我曾经使用过StaggeredGridView和QuiltViewLibrary,并制作了一些不规则的矩形单元格,但不知道如何将它们制作成三角形。 - Ahad Porkar

1

我们需要考虑两件事:

1>像您提到的那样显示不同形状的图像:

您需要在Android中使用可缩放矢量图形。

尝试使用 CustomShapeImageView 库来实现。

逻辑:例如,您需要显示4个倾斜的图像,您只需使用这个 CustomShapeImageView 库编程地创建4个 Framelayout(每个图像一个Framelayout),并在每个 Framelayout 中放置一个 imageview

2>对每个图像进行单击事件处理:

对每个图像进行单击事件处理!您可以尝试使用 OnTouchListener


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接