Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Making skewed view in android

Tags:

android

Im looking for a way to make some type of skewed view in android(Imageview , Button or frame)

anything that i can implement onclick on it.

there is some style in windows phone like this :

enter image description here

or something like this :

enter image description here

Each part of screen (above black line or under it ) are clickable and make new activity apears.I have no problem to show grid with different column size with using staggeredgridview. but i guess for this approach i should make costume layout frame and not grid ?

i find another sample of this on play.google.com :

enter image description here

its some kinda cover making somewhere and image can be changed but without onclick ability on each view.

Edit : Deploy Suggestion from 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;
    }
    } 

and layout :

<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>

but getting this error :

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

ive tried to add the whole method inside another activity but that didnt help . ive add the method inside class because of the word "same OnTouchListener to all the ImageViews" you mentioned.

like image 242
Ahad Porkar Avatar asked Jan 02 '14 14:01

Ahad Porkar


2 Answers

The best solution is to divide a normalised image into polygons where each polygons represents a clickable area. You then attach a OnTouchListener and check if the normalized touch coordinate is within the polygon. This solution requires a bit of work from the programmer plus the implementation of a geometrical algorithm. I will not go further with this solution, but instead present you with the easiest one that takes you the least amount of time to implement. I am guessing that this is what you want, I hope I'm right :).

The easiest solution without third party libraries

Note that this solution is not memory effecient and should be used with caution

1. You cut up your image into several images of the same size where each image only contains a single clickable area and the rest is transparent. If I use the first image in your question as an example then one of these images will look like this:

sample
(source: magma.is)

2. Now you overlay these images in multiple overlapping ImageViews

....
<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. In your Activity/Fragment you set the same OnTouchListener to all the ImageViews. There you discover by transparency elimination which ImageView was truly touched and then you have the information you need about which area of the image (or button if you will) was pressed.

@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) {
            ...

If you only want a user tap you can eiter continue using OnClick in parallel or better, use the MotionEvent.ACTION_UP action and check for tap drift and max time delay between the DOWN and UP motion events. Something like this in MotionEvent.ACTION_UP: ( and note that only the "correct" ImageView will give you this motion event since we discarded further motion events for the other views )

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!
...
like image 118
Ernir Erlingsson Avatar answered Oct 11 '22 04:10

Ernir Erlingsson


You have to customize layouts, let me point you to one as an example

QuiltViewLIbrary

StaggeredGridView

StaggeredGridView is more like Pinteerest's. So you will use one of those as template and code a new GridLayout Class that puts the images in an change to make trapezoids or triangles.

No, its not a non-grid layout..you have to customize a gridlayout.

The tilling math will be somewhat Penrose, do a google search and you will find java libs for that purpose.

like image 29
Fred Grott Avatar answered Oct 11 '22 02:10

Fred Grott