[Android]onTouch in child View is not responding - android

I have been working on making Sudoku grid whose cell's value changes whenever I touch on a cell. So I have implemented this sudoku grid in a LinearLayout by Child View, and tried using OnTouch method, but it is not working. I tried using log method to check whether onTouch is actually called, but it seemes that this method is perfectly ignored. I have been searching for solutions on other question, but it seems none of those solutions helped. I feel kinda suck here, and any help would be greatly appreciated.
Here is my code:
SudokuActivity.java
package snacker.nonogramsolver;
import ...; /*many things are imported here*/
public class SudokuActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sudoku);
Button btn = (Button)findViewById(R.id.btn_clear);
Sudoku sdk = new Sudoku(this);
sdk.setOnTouchListener(sdk);
}
}
;
Sudoku.java
package snacker.nonogramsolver;
import ...;
public class Sudoku extends View implements View.OnTouchListener {
int mWidth = 9;
int mHeight = 9;
int mCellWidth, mCellHeight;
int mCellMargin;
int mEdgeThick;
int mStatus;
int mTextSize;
int mXNow = -1, mYNow = -1;
int[][] mBoard = new int[9][9];
Point mBoardPt;
Paint mTextPaint, mTileEdgePaint;
final static int VALID = 0;
public Sudoku(Context context){
super(context);
initializeBoard();
}
public Sudoku(Context context, AttributeSet attrs){
super(context, attrs);
initializeBoard();
}
#Override
protected void onDraw(Canvas canvas){
/* There are some codes here */
Log.d("LogTest","OnDraw Complete");
}
public void initializeBoard(){
for (int x=0; x< mWidth; x++){
for (int y=0; y< mHeight; y++){
mBoard[x][y] = 0;
}
}
invalidate();
}
public boolean onTouch(View v, MotionEvent event){
Log.d("LogTest","Touched?"); /* LOG NOT ACTIVE HERE */
if(event.getAction() == MotionEvent.ACTION_DOWN){
mXNow = getBoardX(event.getX());
Log.d("LogTest","" + mXNow); /* LOG NOT ACTIVE HERE */
mYNow = getBoardY(event.getY());
Log.d("LogTest","" + mYNow); /* LOG NOT ACTIVE HERE */
mBoard[mXNow][mYNow] = mBoard[mXNow][mYNow] + 1;
invalidate();
return true;
}
else return false;
}
int getBoardX(float scrx){
int x = (int)((scrx) / mCellWidth);
if (x < 0) x = 0;
if (x > 8) x= 8;
return x;
}
int getBoardY(float scry){
int y = (int)((scry) / mCellHeight);
if (y < 0) y = 0;
if (y > 8) y = 8;
return y;
}
}
Edit: added activity XML file.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:id="#+id/activity_sudoku"
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="snacker.nonogramsolver.SudokuActivity">
<snacker.nonogramsolver.Sudoku
android:id="#+id/SudokuGrid"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<Button
android:id="#+id/btn_clear"
android:layout_width="150dp"
android:layout_height="30dp"
android:layout_weight="0.06"
android:text="Clear" />
</LinearLayout>

You cannot directly add touchListener by just creating object of
Sudoku class. You should add view in xml or programatically.
Your Activity
public class MyActivity extends Activity{
#Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
//initializing custom views
MyCustomView1 myCustomView1 = new MyCustomView1(parameterList);
MyCustomView2 myCustomView2 = new MyCustomView2(parameterList);
//adding both custom views to the main activity
mainView.addView(myCustomView1);
mainView.addView(myCustomView1);
//adding custom listener to the custom view 1
myCustomView1.setCustomEventListener(new OnCustomEventListener() {
#Override
public void onEvent() {
//firing an event of custom view 1
Toast.makeText(MainActivity.this, "Touched custom view 1",
Toast.LENGTH_SHORT).show();
}
});
//adding custom listener to the custom view 2
myCustomView2.setCustomEventListener(new OnCustomEventListener() {
#Override
public void onEvent() {
//firing an event of custom view 2
Toast.makeText(MainActivity.this, "Touched custom view 2",
Toast.LENGTH_SHORT).show();
}
});
}
}
Your CustomView 1
public class MyCustomView1 extends LinearLayout{
OnCustomEventListener myCustomEventListener;
public MyCustomView1(ParameterList){
super(ContextFromParameterList);
//Just adding something to the custom view 1 in order to distinguish it on the screen
TextView tv = new TextView(ContextFromParameterList);
tv.setText("Hello world from custom view 1");
addView(tv);
this.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
//delegating one event to another (delegating touch event to custom event)
if (MyCustomView1.this.myCustomEventListener != null)
MyCustomView1.this.myCustomEventListener.onEvent();
return false;
}
}); }
public void setCustomEventListener(OnCustomEventListener
eventListener) {
//setting custom listener from activity
myCustomEventListener = eventListener; } }
Your CustomView2
public class MyCustomView2 extends LinearLayout {
OnCustomEventListener myCustomEventListener;
public MyCustomView2(ParameterList) {
super(ContextFromParameterList);
//Just adding something to the custom view 1 in order to distinguish it on the screen
TextView tv = new TextView(ContextFromParameterList);
tv.setText("Hello world from custom view 2");
addView(tv);
this.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
//delegating one event to another (delegating touch event to custom event)
if (MyCustomView2.this.myCustomEventListener != null)
MyCustomView2.this.myCustomEventListener.onEvent();
return false;
}
});
}
public void setCustomEventListener(OnCustomEventListener eventListener) {
//setting custom listener from activity
myCustomEventListener = eventListener;
}
}
Your listener interface:
public interface OnCustomEventListener{
//interface defines one method. Can be more and methods may have parameters
public void onEvent();
}

Related

Android app does not get into onTouch method

I'm having troubles with the method onTouch(). The code I'm using to implement it is the following.
public class EjemploView extends View implements OnTouchListener {
...
#Override
public boolean onTouch(View v, MotionEvent event) {
if(event.getActionMasked()==0) {
slider2.setCenX(slider2.getCenX() - 1);
}
return false;
}
}
The point is that I set a debugger stop inside onTouch method and even if I touch the screen it will never go inside it. What am I doing wrong?
Adding as an answer, simply so that it makes more sense.
You are implementing onTouchListener but you dont set it as your onTouchListener. Thus you are left with a fully functioning onTouchListener in your view that does not ever get called.
public EjemploView(Context context) {
super(context);
/** \/ Add this line right here \/ */
setOnTouchListener(this);
Resources res = context.getResources();
drawableFader = res.getDrawable(R.drawable.fader);
drawableSlider=res.getDrawable(R.drawable.slider);
fader1 = new Grafico(this, drawableFader);
fader2 = new Grafico(this, drawableFader);
slider1 = new Grafico(this, drawableSlider);
slider2 = new Grafico(this, drawableSlider);
fader1.setAlto(350);
fader1.setAncho(64);
fader2.setAlto(350);
fader2.setAncho(64);
fader2.setAngulo(90);
slider1.setAlto(90);
slider1.setAncho(55);
slider2.setAlto(90);
slider2.setAncho(55);
slider2.setAngulo(90);
}
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//setContentView(R.layout.activity_main);
setContentView(new EjemploView(this));
That's the place where I instantiate the view
public class EjemploView extends View implements OnTouchListener {
private Drawable drawableFader, drawableSlider;
private Grafico fader1, fader2, slider1, slider2;
public EjemploView(Context context) {
super(context);
Resources res = context.getResources();
drawableFader = res.getDrawable(R.drawable.fader);
drawableSlider=res.getDrawable(R.drawable.slider);
fader1 = new Grafico(this, drawableFader);
fader2 = new Grafico(this, drawableFader);
slider1 = new Grafico(this, drawableSlider);
slider2 = new Grafico(this, drawableSlider);
fader1.setAlto(350);
fader1.setAncho(64);
fader2.setAlto(350);
fader2.setAncho(64);
fader2.setAngulo(90);
slider1.setAlto(90);
slider1.setAncho(55);
slider2.setAlto(90);
slider2.setAncho(55);
slider2.setAngulo(90);
}
#Override
protected void onSizeChanged(int ancho, int alto, int ancho_anter, int alto_anter) {
super.onSizeChanged(ancho, alto, ancho_anter, alto_anter);
// Una vez que conocemos nuestro ancho y alto.
fader1.setCenX(ancho / 4 - 31);
fader1.setCenY(alto / 4 - 50);
slider1.setCenX(ancho / 4 - 28);
slider1.setCenY(alto / 4 - 50);
fader2.setCenX(ancho - 271);
fader2.setCenY(alto / 4 - 50);
slider2.setCenX(ancho - 277);
slider2.setCenY(alto / 4 - 50);
}
#Override
synchronized protected void onDraw(Canvas canvas) {
fader1.dibujaGrafico(canvas);
fader2.dibujaGrafico(canvas);
slider1.dibujaGrafico(canvas);
slider2.dibujaGrafico(canvas);
}
#Override
public boolean onTouch(View v, MotionEvent event) {
Log.d("TAG", "I've reached this point");
if(event.getActionMasked()==0) {
slider2.setCenX(slider2.getCenX() - 2);
}
return false;
}
}
And that's my whole code for the view. It is displaying it because I can see the drawables.
You are implementing a new interface, but you should use the already defined onTouchEvent(MotionEvent event). Just override it.
class CustomImageView extends ImageView {
...
#Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getActionMasked();
Log.d(TAG, String.valueOf(action));
return false;
}
}

(Android) Making Images Visible/Not Visible when touched

This is my updated code. It doesn't detect movement at all now. Maybe I shouldn't be making each Image an instance? Basically I want to user to be able to swipe through all the images to make them dissapear.
Thanks for all the help.
package com.picomputing.mythirdapplication;
import android.content.Context;
import android.graphics.Color;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;
/**
* Created by Paul on 8/13/13.
*/
public class Pin extends ImageView implements View.OnTouchListener {
boolean isPinDown;
public Pin(Context context) {
super(context);
this.isPinDown = false;
}
public Pin(Context context, AttributeSet attrs) {
super(context, attrs);
this.isPinDown = false;
}
public Pin(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
this.isPinDown = false;
}
public boolean pinDown() {
return this.isPinDown;
}
public void setPinDown() {
this.isPinDown = true;
}
public void setPinUp() {
this.isPinDown = false;
}
public void togglePin() {
if (isPinDown == false)
{
isPinDown = true;
this.setImageResource(Color.TRANSPARENT);
}
else
{
isPinDown = false;
this.setImageResource(R.drawable.pin);
}
}
#Override
public boolean onTouch(View view, MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_MOVE:
int x = (int) event.getX(); //--relative to mLayout--
int y = (int) event.getY(); //--relative to mLayout--
Rect r = new Rect();
view.getHitRect(r);
if(r.contains(x,y) && view instanceof ImageView){
togglePin();
}
}
return true;
}
}
You need to listen and consume ACTION_MOVE events, for the parent view of whatever you are trying to change.
Here's an example with a couple of ImageViews in a LinerLayout as a parent:
public class test extends Activity {
LinearLayout mLayout;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mLayout = new LinearLayout(this);
mLayout.setOrientation(LinearLayout.VERTICAL);
for(int i = 0 ; i < 5; i++){
ImageView iv = new ImageView(this);
iv.setImageResource(android.R.drawable.ic_dialog_info);
mLayout.addView(iv);
}
setContentView(mLayout);
mLayout.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View view, MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_MOVE:
int x = (int) event.getX(); //--relative to mLayout--
int y = (int) event.getY(); //--relative to mLayout--
Rect r = new Rect();
for(int i = 0 ; i < mLayout.getChildCount(); i++){
View v = mLayout.getChildAt(i);
v.getHitRect(r);
if(r.contains(x,y) && v instanceof ImageView){
((ImageView) v).setImageResource(android.R.drawable.ic_dialog_alert);
}
}
}
return true; //-- this means that view is interested in more events of all kinds--
}
});
}
}
I hope I didn't misunderstand your question
but if what you want to do is to prevent multitoch on the image you can add this attribute
android:splitMotionEvents="false"
in the xml in the parent view of the imageview. for example :
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:splitMotionEvents="false"
>
// YOUR IMAGE VIEW HERE
</LinearLayout>
if you have any question feel free to ask in the comment :)
there are mainly three events on OnTouch action_down,Action_move and Action_up. do your coding on action down event i.e when user has touched your view. see the example here:
#Override
public boolean onTouch(View arg0, MotionEvent arg1) {
if (arg1.getAction()==MotionEvent.ACTION_DOWN) {
//write your code here
}
else {
if (arg1.getAction()==MotionEvent.ACTION_MOVE){
do things
}
else {
if (arg1.getAction()==MotionEvent.ACTION_UP){
do things
}
}
}

Android onClick only works once

So I'm realy confused
I am having a View(R.layout.main) which includes a custom view (canvas)
this View contains a button which is overlayed over the canvas
but when I click the button the OnClicklistener fires the event but after that button is doing nothing when clicked
Activity :
public class RunActivity extends Activity implements OnTouchListener, OnClickListener {
static int width;
static int height;
static boolean reset=false;
//draw d;
View d;
Button jump_button;
//jump
float last_touchpos=0;
static boolean jump=false;
private static Context mContext;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//d = new draw(this);
d = getLayoutInflater().inflate(R.layout.main, null);
d.setOnTouchListener(this);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
mContext = this;
//get screen size
WindowManager wm = (WindowManager) this.getContext().getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
width = display.getWidth(); // deprecated
height = display.getHeight(); // deprecated
setContentView(d);
jump_button = (Button) findViewById(R.id.jump);
jump_button.setOnClickListener(this);
}
public static Context getContext(){
return mContext;
}
#Override
public boolean onTouch(View v, MotionEvent event) {
Log.d("touch","touched");
if (draw.end == true)
{
reset=true;
}
else
{
if(last_touchpos != 0)
{
if(last_touchpos < event.getY())
{
jump = true;
last_touchpos = 0;
}
}
else
{
last_touchpos = event.getY();
}
}
return false;
}
#Override
public void onClick(View arg0) {
jump = true;
}
}
Layout :
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<run.alexander.fuchs.draw
android:id="#+id/canvasview"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<Button
android:id="#+id/jump"
android:layout_width="100dp"
android:layout_height="50dp"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:text="Jump" />
</RelativeLayout>
static boolean jump=false;
remove static from this statement
boolean jump=false;
How can you sure your onClick is called once. Use a log print message within onClick method to make sure that it is called once. Your code is okay, and I hope your onClick works properly and check your rest of code.

Android- Multiple Views not being displayed.(Only first view displays)

I have searched a great deal and have not found a solution to my problem. When I create multiple views and try to add them to a LinearLayout only the first view (cake) displays.
Here is where I create and add the views.
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
LinearLayout linearLayout = (LinearLayout) findViewById(R.id.image_View);
PlayAreaView cake = new PlayAreaView(SecondTestActivity.this, R.drawable.cake);
views.add(cake);
PlayAreaView bomb = new PlayAreaView(SecondTestActivity.this, R.drawable.bomb);
views.add(bomb);
PlayAreaView crown = new PlayAreaView(SecondTestActivity.this, R.drawable.crown);
views.add(crown);
PlayAreaView scissors = new PlayAreaView(SecondTestActivity.this, R.drawable.cut);
views.add(scissors);
PlayAreaView trash = new PlayAreaView(SecondTestActivity.this, R.drawable.bin_closed);
views.add(trash);
PlayAreaView key = new PlayAreaView(SecondTestActivity.this, R.drawable.bullet_key);
views.add(key);
LayoutParams params
= new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
for(View v : views){
Log.v("created", "view created");
v.setLayoutParams(params);
linearLayout.addView(v);
}
}
Here is my main.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/main_View"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<LinearLayout
android:id="#+id/image_View"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal" >
</LinearLayout>
</FrameLayout>
I can create a single view and be fine but I am unable to add multiple views the the LinearLayout. Why is this?
If you look here, there was another person with basically the same problem. However, they were not declaring the orientation of their layout so it defaulted to horizontal. In your layout you have explicitly declared horizontal. Is this intended (for example to have the items show up side-by-side)? If not, change the orientation to vertical and you should be good.
If you need them to show side-by-side, then I am not sure off the top of my head how to do that, but I would guess you need to declare each view as next to the view placed before it (e.g. using something like 'alignToRightOf'. Again, this is just a stab-in-the-dark but it may get you going on a correct path.
Hope this helps.
I found the answer to my problem. I did not fully understand how the Activity handled views. For me to draw multiple separate views I have to loop over each view that I add to an array and call an overridden draw method in the custom view. After I understood this I was able to create multiple views and add separate dragging functions on each view. Here's the code.
public class ThirdTestActivity extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
LinearLayout layout = (LinearLayout) findViewById(R.id.main_View);
layout.addView(new MyCircles(this));
}
private class MyCircles extends View{
private Context myContext;
private ArrayList<MyCircle> circles = new ArrayList<MyCircle>();
private int size = 10;
public MyCircles(Context context) {
super(context);
myContext = context;
addCircles();
}
private void addCircles(){
for (int i = 0; i < size; i++){
circles.add(new MyCircle(myContext, R.drawable.skullcrossbones, i * 40, 50));
}
}
#Override
protected void onDraw(Canvas canvas){
for (View v : circles){
v.draw(canvas);
}
}
#Override
public boolean onTouchEvent(MotionEvent event)
{
int mouseX = (int)event.getX();
int mouseY = (int)event.getY();
MyCircle image = null;
for(MyCircle images : circles){
//Log.v("image checked X: " + images.imageX + ", Y: " + images.imageY, "checked");
// Is the event inside of this view?
if(images.getImageRect().contains((int)event.getX(), (int)event.getY()))
{
image = images;
}
}
if (image != null){
if(event.getAction() == MotionEvent.ACTION_DOWN)
{
Log.v("touched down", "touched down at X: " + mouseX + ", Y: " + mouseY);
image.dragDistance = new Point(mouseX, mouseY);
bringToFront();
isSelected();
return true;
}
else if(event.getAction() == MotionEvent.ACTION_MOVE)
{
Log.v("move", "moving to X: " + mouseX + ", Y: " + mouseY);
image.dragDistance.set(mouseX, mouseY);
invalidate();
return true;
}
}
return super.onTouchEvent(event);
}
}
private class MyCircle extends View{
private int imageId;
private Drawable image;
private Context myContext;
private int size = 48;
private int imageOffset = size/2;
private int imageX;
private int imageY;
private Point dragDistance;
public MyCircle(Context context, int id, int x, int y) {
super(context);
myContext = context;
imageId = id;
imageX = x;
imageY = y;
dragDistance = new Point(imageX + imageOffset, imageY + imageOffset);
}
public Rect getImageRect(){
return image.getBounds();
}
#Override
public void draw(Canvas canvas) {
//Log.v("draw","drawn");
super.onDraw(canvas);
image = myContext.getResources().getDrawable(imageId);
imageX = (dragDistance.x - imageOffset);
imageY = (dragDistance.y - imageOffset);
image.setBounds(imageX, imageY, imageX + size, imageY + size);
image.draw(canvas);
}
}
}
This is written for Android version 2.1 API 7

Android: drag item out of scrolling list/gallery

I want to implement a Gallery that allows the user to drag items out of it. This shouldn't get in the way of scrolling/flinging.
Given the interface layout, the user can only drag items out of the Gallery in a vertical path, and scroll the Gallery horizontally.
Is this feasible? Is there an easy way of detecting horizontal movements, and defer them to the Gallery's event handlers, and intercept vertical movements? Or do I have to override onInterceptTouchEvent() and do the math myself?
(edit: I'm giving a try to a GestureListener, overriding onFling and onScroll, and passing the events to the Gallery when the vertical scroll distance is below a threshold)
I inherited Gallery, and overrode the onScroll method. I haven't implemented the drop logic yet, but the dragging and scrolling work.
When I can spare the time, I'll write a full post in my blog with more details, and the drop mechanism. For now, a simple copy-paste in case somebody reaches this page in the future.
To keep the behavior where it belongs, I created this DraggableView interface:
public interface DraggableView {
public void beforeDrag();
public DragView createDragView();
public Object getDraggedInfo();
public void afterDrop();
}
Views in the Gallery can be dragged out of the Gallery area if they implement this view. They are notified before and after, and must implement two methods:
createDragView() returns a DragView object. Basically, a transparent hovering bitmap to accompany the user's movement.
getDraggedInfo() returns the information that should reach the drop target.
Here's the DragView class:
public class DragView extends ImageView {
private final LayoutParams mLayoutParams;
public DragView(Context context, Bitmap bitmap) {
super(context);
mLayoutParams = new LayoutParams();
mLayoutParams.gravity = Gravity.TOP | Gravity.LEFT;
mLayoutParams.height = LayoutParams.WRAP_CONTENT;
mLayoutParams.width = LayoutParams.WRAP_CONTENT;
mLayoutParams.flags = LayoutParams.FLAG_NOT_FOCUSABLE
| LayoutParams.FLAG_NOT_TOUCHABLE;
mLayoutParams.format = PixelFormat.TRANSLUCENT;
mLayoutParams.windowAnimations = 0;
mLayoutParams.alpha = 0.5f;
setImageBitmap(bitmap);
setLayoutParams(mLayoutParams);
}
public void move(int x, int y) {
mLayoutParams.x = x;
mLayoutParams.y = y;
}
}
As you can see, it takes a Bitmap in construction, and creates a hovering ImageView. Finally, here is the (just implemented and not very clean) Gallery code to make it all happen:
public class DraggableItemGallery extends Gallery {
private boolean mDragging;
private DragView mDragView;
private DraggableView mDragViewOwner;
private WindowManager mWindowManager;
private boolean mScrollStarted;
public DraggableItemGallery(Context context) {
super(context);
initialize();
}
public DraggableItemGallery(Context context, AttributeSet attrs) {
super(context, attrs);
initialize();
}
public DraggableItemGallery(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initialize();
}
public void initialize() {
mWindowManager = (WindowManager)
getContext().getSystemService("window");
}
private void startDraggingItem(DraggableView view, int x, int y) {
mDragging = true;
mDragViewOwner = view;
mDragView = view.createDragView();
mDragView.move(x, y);
mWindowManager.addView(mDragView, mDragView.getLayoutParams());
}
private void continueDraggingItem(int x, int y) {
DragView dragView = getDragView();
dragView.move(x, y);
mWindowManager.updateViewLayout(dragView, dragView.getLayoutParams());
}
private void stopDraggingItem() {
mDragging = false;
mWindowManager.removeView(mDragView);
mDragViewOwner.afterDrop();
mDragView = null;
mDragViewOwner = null;
}
private DraggableView getDraggedItem() {
return mDragViewOwner;
}
private DragView getDragView() {
return mDragView;
}
private boolean isDraggingItem() {
return (mDragging);
}
private void setScrolling(boolean scrolling) {
mScrollStarted = scrolling;
System.out.println("Scrolling " + scrolling);
}
private boolean isScrolling() {
return mScrollStarted;
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if ((event.getAction() & ACTION_MASK) == ACTION_UP) {
setScrolling(false);
if (isDraggingItem())
stopDraggingItem();
}
return super.onTouchEvent(event);
}
final Rect onScroll_tempRect = new Rect();
#Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
if (isScrolling()) {
if (isDraggingItem()) {
int x = (int) e2.getX(),
y = (int) e2.getY();
System.out.println("Moving to " + x + " " + y);
continueDraggingItem(x, y);
return true;
} else {
/* Not dragging, let the Gallery handle the event */
return super.onScroll(e1, e2, distanceX, distanceY);
}
} else {
setScrolling(true);
boolean isVertical = (Math.abs(distanceY) > Math.abs(distanceX));
if (isVertical) {
int x = (int) e1.getX(),
y = (int) e1.getY();
View hitChild = null;
// A tiny optimization, declared above this method
final Rect hitRect = onScroll_tempRect;
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
child.getHitRect(hitRect);
if (hitRect.contains(x, y)) {
hitChild = child;
break;
}
}
if (hitChild instanceof DraggableView) {
startDraggingItem((DraggableView) hitChild, x, y);
return true;
}
}
/* Either the scroll is not vertical, or the point
* of origin is not above a DraggableView. Again,
* we let the Gallery handle the event.
*/
return super.onScroll(e1, e2, distanceX, distanceY);
}
}
}
Hope it helps.
Here is something I did to do exactly that. That's only the code for the activity... there is some layout and other res files you'll need...
Every list item has an icon and name matched randomly.
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.Rect;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.widget.FrameLayout.LayoutParams;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.*;
import java.util.ArrayList;
import java.util.Arrays;
public class DragActivity extends Activity implements View.OnTouchListener, AdapterView.OnItemLongClickListener
{
private static final String TAG="DragActivity";
private static final int NOT_DRAGGING = 0;
private static final int DRAGGING = 1;
private int state=NOT_DRAGGING;
private ImageView draggable =null;
private int dragged_position;
float current_x, current_y;
int current_icon = R.drawable.notepad;
private ArrayList<String> names = new ArrayList<String>(Arrays.asList("John", "Mark", "Mathew", "Luke", "Bob", "Will", "Brian", "Mike"));
private ArrayList<Integer> icons = new ArrayList<Integer>(Arrays.asList( R.drawable.glasses, R.drawable.monkey, R.drawable.normal, R.drawable.smile, R.drawable.wink));
private ArrayList<Integer> matching;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
setupListContent();
ListView list = (ListView) findViewById(R.id.main_list);
list.setAdapter(new DragListAdapter());
list.setOnItemLongClickListener(this);
list.setOnTouchListener(this);
// need to use the same view for the both listeners, as described in Android documentation :
// http://developer.android.com/guide/topics/ui/ui-events.html
// onTouch() - This returns a boolean to indicate whether your listener consumes this event. The important thing
// is that this event can have multiple actions that follow each other. So, if you return false when the down action
// event is received, you indicate that you have not consumed the event and are also not interested in subsequent
// actions from this event. Thus, you will not be called for any other actions within the event, such as a finger
// gesture, or the eventual up action event.
ImageView image = (ImageView) findViewById(R.id.main_image);
image.setImageResource(current_icon);
}
private void setupListContent() {
matching = new ArrayList<Integer>();
for (int i=0; i<names.size(); i++) {
matching.add((int) (icons.size() * Math.random()));
}
}
#SuppressWarnings("unchecked")
private class DragListAdapter extends ArrayAdapter {
public DragListAdapter() {
super(DragActivity.this, R.layout.list_item, names);
}
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
if (row == null) {
LayoutInflater inflater = getLayoutInflater();
row = inflater.inflate(R.layout.list_item, parent, false);
}
row.setDrawingCacheEnabled(true);
TextView name = (TextView) row.findViewById(R.id.item_text);
ImageView icon = (ImageView) row.findViewById(R.id.item_icon);
name.setText(names.get(position));
icon.setImageResource(icons.get(matching.get(position)));
return row;
}
}
private boolean checkOnDropIcon(MotionEvent me) {
ImageView drop_icon = (ImageView) findViewById(R.id.main_image);
Rect icon_rect = new Rect();
drop_icon.getGlobalVisibleRect(icon_rect);
Log.d(TAG, "icon at " + icon_rect.left + "<- ->" + icon_rect.right + ", " +
icon_rect.top + " ^ v" + icon_rect.bottom);
if ((me.getRawX()<icon_rect.left) || (me.getRawX()>icon_rect.right) ||
(me.getRawY()<icon_rect.top) || (me.getRawY()>icon_rect.bottom)) {
return false;
}
else {
return true;
}
}
private void checkOnDrop(MotionEvent me) {
boolean onDropIcon = checkOnDropIcon(me);
ImageView image = (ImageView) findViewById(R.id.main_image);
if ((onDropIcon) && (current_icon==R.drawable.notepad)) {
current_icon = R.drawable.exit;
image.setImageResource(current_icon);
image.invalidate();
return;
}
if ((!onDropIcon) && (current_icon==R.drawable.exit)) {
current_icon = R.drawable.notepad;
image.setImageResource(current_icon);
image.invalidate();
return;
}
}
public boolean onTouch(View view, MotionEvent me) {
if (state == NOT_DRAGGING) {
// get the position of the touch so we know where to place the dragging item if it is a long press
current_x = me.getRawX();
current_y = me.getRawY();
return false;
}
else {
FrameLayout frame = (FrameLayout) findViewById(R.id.drag_space);
if (me.getAction()==MotionEvent.ACTION_UP) {
frame.removeAllViews();
draggable=null;
frame.setVisibility(View.GONE);
state=NOT_DRAGGING;
// check if we dropped a name
if (checkOnDropIcon(me)) {
names.remove(dragged_position);
matching.remove(dragged_position);
ListView list = (ListView) findViewById(R.id.main_list);
DragListAdapter adapter = (DragListAdapter) list.getAdapter();
adapter.notifyDataSetChanged();
}
// restore the icon
ImageView image = (ImageView) findViewById(R.id.main_image);
current_icon = R.drawable.notepad;
image.setImageResource(current_icon);
image.invalidate();
}
if (me.getAction()==MotionEvent.ACTION_MOVE) {
int frame_position[] = new int[2];
frame.getLocationOnScreen(frame_position);
draggable.setPadding(
(int) me.getRawX()-frame_position[0]-(draggable.getDrawable().getIntrinsicWidth()/2),
(int) me.getRawY()-frame_position[1]-(draggable.getDrawable().getIntrinsicHeight()/2),
0, 0);
draggable.invalidate();
checkOnDrop(me);
}
return true;
}
}
public boolean onItemLongClick(AdapterView<?> adapterView, View view, int i, long l) {
if (state == DRAGGING) {
Log.d(TAG, "already have an object moving... ?");
return false;
}
FrameLayout frame = (FrameLayout) findViewById(R.id.drag_space);
int frame_position[] = new int[2];
frame.getLocationOnScreen(frame_position);
// setup everything for dragging
state = DRAGGING;
dragged_position = i;
draggable = new ImageView(this);
Bitmap bm = view.getDrawingCache();
draggable.setImageBitmap(bm);
draggable.setAlpha(150);
draggable.setScaleType(ImageView.ScaleType.CENTER);
draggable.setDrawingCacheEnabled(true);
draggable.setPadding((int) current_x-frame_position[0]-(bm.getWidth()/2), (int) current_y-frame_position[1]-(bm.getHeight()/2), 0, 0);
frame.setVisibility(View.VISIBLE);
frame.addView(draggable, new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
return true;
}
}

Categories

Resources