I am trying to implement an ACTION_MOVE input that moves a paddle as long as the screen is touched, in place of the ACTION_DOWN that is currently present. However Once I switch the ACTION_MOVE in place of ACTION_DOWN, the paddle moves but with stutters(some times too slow, then stopping then going too fast all of a sudden). How would I resolve this? Would Moving the Input to another thread be any good?
Here's the code
MainActivity.java
package com.nblsoft.ballpractise;
import android.graphics.Color;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
View myball = new BallDraw(this);
myball.setBackgroundColor(Color.BLACK);
setContentView(myball);
//setContentView(R.layout.activity_main);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
BallDraw.java
package com.nblsoft.ballpractise;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.view.MotionEvent;
import android.view.View;
public class BallDraw extends View {
//Default System Variables
int screenHeight = getResources().getDisplayMetrics().heightPixels;
int screenWidth = getResources().getDisplayMetrics().widthPixels;
private int xMin = 0; // This view's bounds
private int xMax;
private int yMin = 0;
private int yMax;
//Game Variables
public int ball_pos_x = 0;
public int ball_pos_y = 800;
public int ball_vel_x = 5;
public int ball_vel_y = 5;
public int pad_1_pos = 10;
public int pad_2_pos = 10;
public int randi=1;
//Pads Implemented as Array of points
//public int[] pad_1 = {100,200,220,240};
//public int[] pad_2 = {100,200,1250,1270};
Rect pad_1 = new Rect(400 + pad_1_pos,220,500 + pad_1_pos,240);
Rect pad_2 = new Rect(400 + pad_2_pos,1250,500 + pad_1_pos,1270);
Rect pad_1_touch = new Rect(0, 0, 1080, 200);
Rect pad_2_touch = new Rect(0, 1300, 1080, 1500);
//calculations for screen sizes
private Paint paint;
public BallDraw(Context context) {
super(context);
paint = new Paint();
// To enable touch mode
this.setFocusableInTouchMode(true);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
//case MotionEvent.ACTION_MOVE:
if(pad_1_touch.contains(x,y)){
//pad_1[0]+=10;
//pad_1[1]+=10;
pad_1.offset(pad_1_pos,0);
}
else if(pad_2_touch.contains(x,y)){
//pad_2[0]+=10;
//pad_2[1]+=10;
pad_2.offset(pad_2_pos,0);
}
}
return true; //Event Handled
}
#Override
protected void onDraw(Canvas canvas) {
paint.setColor(Color.WHITE);
//Touch Regions (Un-comment to see touch Regions)
//canvas.drawRect(pad_1_touch,paint);
//canvas.drawRect(pad_2_touch,paint);
//Draw Border
canvas.drawRect(0,800,1080,803,paint);
//Paddles Draw code
canvas.drawRect(pad_1,paint);
canvas.drawRect(pad_2,paint);
//canvas.drawRect(pad_1[0],pad_1[2], pad_1[1], pad_1[3], paint);
//canvas.drawRect(pad_2[0],pad_2[2],pad_2[1],pad_2[3],paint);
//Ball code
canvas.drawRect(ball_pos_x,ball_pos_y,ball_pos_x + 13, ball_pos_y+13,paint);
// Update the position of the ball, including collision detection and reaction.
update();
// Delay
try {
Thread.sleep(30);
} catch (InterruptedException e) { }
invalidate();
}
#Override
public void onSizeChanged(int w, int h, int oldW, int oldH) {
// Set the movement bounds for the ball
xMax = w-1;
yMax = h-1;
}
private void update(){
ball_pos_y += ball_vel_y;
ball_pos_x += ball_vel_x;
if(pad_1.contains(ball_pos_x,ball_pos_y)){
ball_vel_y = -ball_vel_y;
}
else if(pad_2.contains(ball_pos_x,ball_pos_y)){
ball_vel_y = -ball_vel_y;
}
else if(ball_pos_x >= xMax){
ball_vel_x = -ball_vel_x;
}
else if(ball_pos_y >= yMax){
ball_init();
}
else if(ball_pos_x <= 0){
ball_vel_x = -ball_vel_x;
}
else if(ball_pos_y <= 0){
ball_init();
}
}
private void ball_init(){
if (randi == 1){
randi = -1;
}
else{
randi= 1;
}
ball_pos_x=0;
ball_pos_y=800;
ball_vel_x=5;
ball_vel_y=5 * randi;
}
}
Related
I know its possible to paint the background of canvas using
mPaint = new Paint();
mPaint.setColor(Color.RED);
Im just wondering how to i set a permanent background for it. Ive tried using the xml file but nothing happens. Any ideas?
This is the source code of the project, ive been following a tutorial how to do it because im fairly unfamiliar with bitmaps.
Canvas Class
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ImageView;
public class GameBoard extends View{
private int mFlagX = -1;
private int mFlagY = -1;
private Bitmap mBitmap = null;
private Bitmap nBitmap = null;
private Paint mPaint = null;
private boolean isFlagHidden = false;
private int mBoundX = -1;
private int mBoundY = -1;
//play with these values to make the app more or less challenging
public final int CLOSER = 50;
public final int CLOSE = 100;
public GameBoard(Context context, AttributeSet aSet) {
super(context, aSet);
//load our bitmap
mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.star);
//create a paint brush
mPaint = new Paint();
mPaint.setColor(Color.RED);
}
#Override
public void onDraw(Canvas canvas) {
//initialize
if ((mFlagX < 1) || (mFlagY < 1)) {
mFlagX = (int) (getWidth() / 2) - mBitmap.getWidth() / 2;
mFlagY = (int) (getHeight() / 2) - mBitmap.getHeight() / 2;
mBoundX = (int)getWidth() - mBitmap.getWidth();
mBoundY = (int)getHeight() - mBitmap.getHeight();
}
//draw background
canvas.drawRect(0, 0, getWidth(), getHeight(), mPaint);
//draw the flag
if (!isFlagHidden) {
canvas.drawBitmap(mBitmap, mFlagX, mFlagY, null);
}
}
public void hideTheFlag(){
//randomize flag location
mFlagX = (int) Math.ceil(Math.random() * mBoundX);
mFlagY = (int) Math.ceil(Math.random() * mBoundY);
isFlagHidden = true;
//force redraw
invalidate();
}
public void giveUp(){
isFlagHidden = false;
//force redraw
invalidate();
}
public Indicators takeAGuess(float x, float y) {
//this is our "warm" area
Rect prettyClose = new Rect(mFlagX - CLOSE, mFlagY - CLOSE, mFlagX+mBitmap.getWidth() + CLOSE, mFlagY+mBitmap.getHeight() + CLOSE);
//normalize
if (prettyClose.left < 0) prettyClose.left = 0;
if (prettyClose.top < 0) prettyClose.top = 0;
if (prettyClose.right > mBoundX) prettyClose.right = mBoundX;
if (prettyClose.bottom > mBoundY) prettyClose.bottom = mBoundY;
//this is our "hot" area
Rect reallyClose = new Rect(mFlagX - CLOSER, mFlagY - CLOSER, mFlagX+mBitmap.getWidth() + CLOSER, mFlagY+mBitmap.getHeight() + CLOSER);
//normalize
if (reallyClose.left < 0) reallyClose.left = 0;
if (reallyClose.top < 0) reallyClose.top = 0;
if (reallyClose.right > mBoundX) reallyClose.right = mBoundX;
if (reallyClose.bottom > mBoundY) reallyClose.bottom = mBoundY;
//this is the area that contains our flag
Rect bullsEye = new Rect(mFlagX, mFlagY, mFlagX+mBitmap.getWidth(), mFlagY+mBitmap.getHeight());
//check to see where on the board the user pressed
if (bullsEye.contains((int) x, (int)y)) {
//found it
isFlagHidden = false;
invalidate();
return Indicators.BULLSEYE;
} else if (reallyClose.contains((int) x, (int)y)) {
//hot
return Indicators.HOT;
} else if (prettyClose.contains((int)x, (int)y)) {
//warm
return Indicators.WARM;
} else {
//not even close
return Indicators.COLD;
}
}
}
Game Class
import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;
import android.widget.Button;
import android.widget.TextView;
public class FindTheStar extends Activity implements OnTouchListener, OnClickListener{
private GameBoard mGameBoard = null;
private boolean isFlagHidden = false;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_star);
mGameBoard = (GameBoard) findViewById(R.id.Hide_canvas);
mGameBoard.setOnTouchListener(this);
Button b = (Button) findViewById(R.id.the_button);
b.setOnClickListener(this);
}
#Override
public boolean onTouch(View v, MotionEvent event) {
if (v.getId() == R.id.Hide_canvas) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
if (isFlagHidden) {
TextView tv = (TextView)findViewById (R.id.the_label);
switch (mGameBoard.takeAGuess(event.getX(), event.getY())) {
case BULLSEYE:
Button b = (Button) findViewById(R.id.the_button);
isFlagHidden = false;
b.setText("Go Hide!");
tv.setText("You found me!");
tv.setTextColor(Color.GREEN);
break;
case HOT:
tv.setText("You're hot!");
tv.setTextColor(Color.RED);
break;
case WARM:
tv.setText("Getting warm...");
tv.setTextColor(Color.YELLOW);
break;
case COLD:
tv.setText("You're cold.");
tv.setTextColor(Color.BLUE);
break;
}
}
}
return true;
}
return false;
}
#Override
public void onClick(View v) {
if (v.getId() == R.id.the_button) {
TextView tv = (TextView)findViewById (R.id.the_label);
tv.setText("");
Button b = (Button) findViewById(R.id.the_button);
isFlagHidden = !isFlagHidden;
if (isFlagHidden) {
b.setText("Can't find me?");
mGameBoard.hideTheFlag();
} else {
b.setText("Go Hide!");
mGameBoard.giveUp();
}
}
}
}
XML File
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView
android:id="#+id/the_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:textSize="20sp"
android:layout_marginBottom="10dip"
android:text="Lets Play Hide and Seek!"/>
<Button
android:id="#+id/the_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:layout_marginBottom="10dip"
android:text="Go Hide!"/>
<app.autismapp.GameBoard
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="#+id/Hide_canvas"/>
</LinearLayout>
yes you can set your permanent background using xml layout..i done this by creating two class.
this is my code in MainACtivity
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final BrushView view=new BrushView(this);
setContentView(R.layout.mylayout);//removed this one if the paint doesnt work
view.setBackgroundResource(R.drawable.background);//to set background
setContentView(view);// to display the background
and my second class
public class PaintView extends View {
private Paint paint = new Paint();
public LayoutParams params;
public PaintView(Context context) {
super(context);
paint.setAntiAlias(true);
paint.setColor(Color.BLUE);
i hope it gives you an idea
I want to implement drag and drop of list-items in my application. I followed this, it works well for lists containing text only. I integrated lazy-list with images along with drag and drop. But, while I drag the item, the dragged item goes to actual position. The item below the dragged item gets removed from the list. After I take my fingers off, dragged item doesn't move to the place I wish. What is wrong with this code?
main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<com.listviewdragginganimation.DynamicListView
android:id="#+id/list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1"/>
<Button
android:id="#+id/button1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Clear Cache"/>
</LinearLayout>
DynamicListView.Java:
package com.listviewdragginganimation;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.TypeEvaluator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewTreeObserver;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.ListView;
import java.util.ArrayList;
public class DynamicListView extends ListView {
private final int SMOOTH_SCROLL_AMOUNT_AT_EDGE = 15;
private final int MOVE_DURATION = 150;
private final int LINE_THICKNESS = 15;
public ArrayList<String> mCheeseList;
private int mLastEventY = -1;
private int mDownY = -1;
private int mDownX = -1;
private int mTotalOffset = 0;
private boolean mCellIsMobile = false;
private boolean mIsMobileScrolling = false;
private int mSmoothScrollAmountAtEdge = 0;
private final int INVALID_ID = -1;
private long mAboveItemId = INVALID_ID;
private long mMobileItemId = INVALID_ID;
private long mBelowItemId = INVALID_ID;
private BitmapDrawable mHoverCell;
private Rect mHoverCellCurrentBounds;
private Rect mHoverCellOriginalBounds;
private final int INVALID_POINTER_ID = -1;
private int mActivePointerId = INVALID_POINTER_ID;
private boolean mIsWaitingForScrollFinish = false;
private int mScrollState = OnScrollListener.SCROLL_STATE_IDLE;
public DynamicListView(Context context) {
super(context);
init(context);
}
public DynamicListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}
public DynamicListView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public void init(Context context) {
setOnItemLongClickListener(mOnItemLongClickListener);
setOnScrollListener(mScrollListener);
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
mSmoothScrollAmountAtEdge = (int) (SMOOTH_SCROLL_AMOUNT_AT_EDGE / metrics.density);
}
private AdapterView.OnItemLongClickListener mOnItemLongClickListener = new AdapterView.OnItemLongClickListener() {
public boolean onItemLongClick(AdapterView<?> arg0, View arg1, int pos,
long id) {
mTotalOffset = 0;
int position = pointToPosition(mDownX, mDownY);
int itemNum = position - getFirstVisiblePosition();
View selectedView = getChildAt(itemNum);
mMobileItemId = getAdapter().getItemId(position);
mHoverCell = getAndAddHoverView(selectedView);
selectedView.setVisibility(INVISIBLE);
mCellIsMobile = true;
updateNeighborViewsForID(mMobileItemId);
return true;
}
};
private BitmapDrawable getAndAddHoverView(View v) {
int w = v.getWidth();
int h = v.getHeight();
int top = v.getTop();
int left = v.getLeft();
Bitmap b = getBitmapWithBorder(v);
BitmapDrawable drawable = new BitmapDrawable(getResources(), b);
mHoverCellOriginalBounds = new Rect(left, top, left + w, top + h);
mHoverCellCurrentBounds = new Rect(mHoverCellOriginalBounds);
drawable.setBounds(mHoverCellCurrentBounds);
return drawable;
}
/** Draws a black border over the screenshot of the view passed in. */
private Bitmap getBitmapWithBorder(View v) {
Bitmap bitmap = getBitmapFromView(v);
Canvas can = new Canvas(bitmap);
Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
Paint paint = new Paint();
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(LINE_THICKNESS);
paint.setColor(Color.BLACK);
can.drawBitmap(bitmap, 0, 0, null);
can.drawRect(rect, paint);
return bitmap;
}
/** Returns a bitmap showing a screenshot of the view passed in. */
private Bitmap getBitmapFromView(View v) {
Bitmap bitmap = Bitmap.createBitmap(v.getWidth(), v.getHeight(),
Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
v.draw(canvas);
return bitmap;
}
private void updateNeighborViewsForID(long itemID) {
int position = getPositionForID(itemID);
LazyAdapter adapter = ((LazyAdapter) getAdapter());
mAboveItemId = adapter.getItemId(position - 1);
mBelowItemId = adapter.getItemId(position + 1);
}
/** Retrieves the view in the list corresponding to itemID */
public View getViewForID(long itemID) {
int firstVisiblePosition = getFirstVisiblePosition();
LazyAdapter adapter = ((LazyAdapter) getAdapter());
for (int i = 0; i < getChildCount(); i++) {
View v = getChildAt(i);
int position = firstVisiblePosition + i;
long id = adapter.getItemId(position);
if (id == itemID) {
return v;
}
}
return null;
}
/** Retrieves the position in the list corresponding to itemID */
public int getPositionForID(long itemID) {
View v = getViewForID(itemID);
if (v == null) {
return -1;
} else {
return getPositionForView(v);
}
}
#Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
if (mHoverCell != null) {
mHoverCell.draw(canvas);
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
mDownX = (int) event.getX();
mDownY = (int) event.getY();
mActivePointerId = event.getPointerId(0);
break;
case MotionEvent.ACTION_MOVE:
if (mActivePointerId == INVALID_POINTER_ID) {
break;
}
int pointerIndex = event.findPointerIndex(mActivePointerId);
mLastEventY = (int) event.getY(pointerIndex);
int deltaY = mLastEventY - mDownY;
if (mCellIsMobile) {
mHoverCellCurrentBounds.offsetTo(mHoverCellOriginalBounds.left,
mHoverCellOriginalBounds.top + deltaY + mTotalOffset);
mHoverCell.setBounds(mHoverCellCurrentBounds);
invalidate();
// Log.v("handle switch 1","1");
handleCellSwitch();
// Log.v("handle switch 2","2");
mIsMobileScrolling = false;
handleMobileCellScroll();
return false;
}
break;
case MotionEvent.ACTION_UP:
touchEventsEnded();
break;
case MotionEvent.ACTION_CANCEL:
touchEventsCancelled();
break;
case MotionEvent.ACTION_POINTER_UP:
pointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
final int pointerId = event.getPointerId(pointerIndex);
if (pointerId == mActivePointerId) {
touchEventsEnded();
}
break;
default:
break;
}
return super.onTouchEvent(event);
}
private void handleCellSwitch() {
final int deltaY = mLastEventY - mDownY;
int deltaYTotal = mHoverCellOriginalBounds.top + mTotalOffset + deltaY;
View belowView = getViewForID(mBelowItemId);
View mobileView = getViewForID(mMobileItemId);
View aboveView = getViewForID(mAboveItemId);
boolean isBelow = (belowView != null)
&& (deltaYTotal > belowView.getTop());
boolean isAbove = (aboveView != null)
&& (deltaYTotal < aboveView.getTop());
if (isBelow || isAbove) {
final long switchItemID = isBelow ? mBelowItemId : mAboveItemId;
View switchView = isBelow ? belowView : aboveView;
final int originalItem = getPositionForView(mobileView);
if (switchView == null) {
updateNeighborViewsForID(mMobileItemId);
return;
}
Log.v("swapElements switch 1", "1");
Log.v("swapElements originalItem 1", originalItem + "");
Log.v("swapElements switchView 1", "" + switchView);
setCheeseList(MainActivity.mCheeseList);
Log.v("mCheeseList item", mCheeseList.get(originalItem));
swapElements(mCheeseList, originalItem,
getPositionForView(switchView));
Log.v("swapElements switch 2", "2");
// ((BaseAdapter) getAdapter()).notifyDataSetChanged();
MainActivity.adapter.notifyDataSetChanged();
mDownY = mLastEventY;
final int switchViewStartTop = switchView.getTop();
mobileView.setVisibility(View.VISIBLE);
switchView.setVisibility(View.INVISIBLE);
updateNeighborViewsForID(mMobileItemId);
final ViewTreeObserver observer = getViewTreeObserver();
observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
public boolean onPreDraw() {
observer.removeOnPreDrawListener(this);
View switchView = getViewForID(switchItemID);
mTotalOffset += deltaY;
int switchViewNewTop = switchView.getTop();
int delta = switchViewStartTop - switchViewNewTop;
switchView.setTranslationY(delta);
ObjectAnimator animator = ObjectAnimator.ofFloat(
switchView, View.TRANSLATION_Y, 0);
animator.setDuration(MOVE_DURATION);
animator.start();
return true;
}
});
}
}
private void swapElements(ArrayList<String> arrayList, int indexOne,
int indexTwo) {
Log.v("index one", indexOne + "");
Log.v("index two", indexTwo + "");
Log.v("temp", arrayList.get(indexOne));
String temp = arrayList.get(indexOne);
arrayList.set(indexOne, arrayList.get(indexTwo));
arrayList.set(indexTwo, temp);
}
/**
* Resets all the appropriate fields to a default state while also animating
* the hover cell back to its correct location.
*/
private void touchEventsEnded() {
final View mobileView = getViewForID(mMobileItemId);
if (mCellIsMobile || mIsWaitingForScrollFinish) {
mCellIsMobile = false;
mIsWaitingForScrollFinish = false;
mIsMobileScrolling = false;
mActivePointerId = INVALID_POINTER_ID;
// If the autoscroller has not completed scrolling, we need to wait
// for it to
// finish in order to determine the final location of where the
// hover cell
// should be animated to.
if (mScrollState != OnScrollListener.SCROLL_STATE_IDLE) {
mIsWaitingForScrollFinish = true;
return;
}
mHoverCellCurrentBounds.offsetTo(mHoverCellOriginalBounds.left,
mobileView.getTop());
ObjectAnimator hoverViewAnimator = ObjectAnimator.ofObject(
mHoverCell, "bounds", sBoundEvaluator,
mHoverCellCurrentBounds);
hoverViewAnimator
.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(
ValueAnimator valueAnimator) {
invalidate();
}
});
hoverViewAnimator.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationStart(Animator animation) {
setEnabled(false);
}
#Override
public void onAnimationEnd(Animator animation) {
mAboveItemId = INVALID_ID;
mMobileItemId = INVALID_ID;
mBelowItemId = INVALID_ID;
mobileView.setVisibility(VISIBLE);
mHoverCell = null;
setEnabled(true);
invalidate();
}
});
hoverViewAnimator.start();
} else {
touchEventsCancelled();
}
}
/**
* Resets all the appropriate fields to a default state.
*/
private void touchEventsCancelled() {
View mobileView = getViewForID(mMobileItemId);
if (mCellIsMobile) {
mAboveItemId = INVALID_ID;
mMobileItemId = INVALID_ID;
mBelowItemId = INVALID_ID;
mobileView.setVisibility(VISIBLE);
mHoverCell = null;
invalidate();
}
mCellIsMobile = false;
mIsMobileScrolling = false;
mActivePointerId = INVALID_POINTER_ID;
}
/**
* This TypeEvaluator is used to animate the BitmapDrawable back to its
* final location when the user lifts his finger by modifying the
* BitmapDrawable's bounds.
*/
private final static TypeEvaluator<Rect> sBoundEvaluator = new TypeEvaluator<Rect>() {
public Rect evaluate(float fraction, Rect startValue, Rect endValue) {
return new Rect(interpolate(startValue.left, endValue.left,
fraction), interpolate(startValue.top, endValue.top,
fraction), interpolate(startValue.right, endValue.right,
fraction), interpolate(startValue.bottom, endValue.bottom,
fraction));
}
public int interpolate(int start, int end, float fraction) {
return (int) (start + fraction * (end - start));
}
};
private void handleMobileCellScroll() {
mIsMobileScrolling = handleMobileCellScroll(mHoverCellCurrentBounds);
}
public boolean handleMobileCellScroll(Rect r) {
int offset = computeVerticalScrollOffset();
int height = getHeight();
int extent = computeVerticalScrollExtent();
int range = computeVerticalScrollRange();
int hoverViewTop = r.top;
int hoverHeight = r.height();
if (hoverViewTop <= 0 && offset > 0) {
smoothScrollBy(-mSmoothScrollAmountAtEdge, 0);
return true;
}
if (hoverViewTop + hoverHeight >= height && (offset + extent) < range) {
smoothScrollBy(mSmoothScrollAmountAtEdge, 0);
return true;
}
return false;
}
public void setCheeseList(ArrayList<String> cheeseList) {
mCheeseList = cheeseList;
}
private AbsListView.OnScrollListener mScrollListener = new AbsListView.OnScrollListener() {
private int mPreviousFirstVisibleItem = -1;
private int mPreviousVisibleItemCount = -1;
private int mCurrentFirstVisibleItem;
private int mCurrentVisibleItemCount;
private int mCurrentScrollState;
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
mCurrentFirstVisibleItem = firstVisibleItem;
mCurrentVisibleItemCount = visibleItemCount;
mPreviousFirstVisibleItem = (mPreviousFirstVisibleItem == -1) ? mCurrentFirstVisibleItem
: mPreviousFirstVisibleItem;
mPreviousVisibleItemCount = (mPreviousVisibleItemCount == -1) ? mCurrentVisibleItemCount
: mPreviousVisibleItemCount;
checkAndHandleFirstVisibleCellChange();
checkAndHandleLastVisibleCellChange();
mPreviousFirstVisibleItem = mCurrentFirstVisibleItem;
mPreviousVisibleItemCount = mCurrentVisibleItemCount;
}
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
mCurrentScrollState = scrollState;
mScrollState = scrollState;
isScrollCompleted();
}
private void isScrollCompleted() {
if (mCurrentVisibleItemCount > 0
&& mCurrentScrollState == SCROLL_STATE_IDLE) {
if (mCellIsMobile && mIsMobileScrolling) {
handleMobileCellScroll();
} else if (mIsWaitingForScrollFinish) {
touchEventsEnded();
}
}
}
/**
* Determines if the listview scrolled up enough to reveal a new cell at
* the top of the list. If so, then the appropriate parameters are
* updated.
*/
public void checkAndHandleFirstVisibleCellChange() {
if (mCurrentFirstVisibleItem != mPreviousFirstVisibleItem) {
if (mCellIsMobile && mMobileItemId != INVALID_ID) {
updateNeighborViewsForID(mMobileItemId);
handleCellSwitch();
}
}
}
/**
* Determines if the listview scrolled down enough to reveal a new cell
* at the bottom of the list. If so, then the appropriate parameters are
* updated.
*/
public void checkAndHandleLastVisibleCellChange() {
int currentLastVisibleItem = mCurrentFirstVisibleItem
+ mCurrentVisibleItemCount;
int previousLastVisibleItem = mPreviousFirstVisibleItem
+ mPreviousVisibleItemCount;
if (currentLastVisibleItem != previousLastVisibleItem) {
if (mCellIsMobile && mMobileItemId != INVALID_ID) {
updateNeighborViewsForID(mMobileItemId);
handleCellSwitch();
}
}
}
};
}
LazyAdapter.java:
package com.listviewdragginganimation;
import java.util.ArrayList;
import android.app.Activity;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
public class LazyAdapter extends BaseAdapter {
private Activity activity;
private ArrayList<String> data=new ArrayList<String>();
private static LayoutInflater inflater=null;
public ImageLoader imageLoader;
public LazyAdapter(MainActivity a, ArrayList<String> mCheeseList) {
// TODO Auto-generated constructor stub
activity = a;
data=mCheeseList;
inflater = (LayoutInflater)activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
imageLoader=new ImageLoader(activity.getApplicationContext());
}
public int getCount() {
return data.size();
}
public Object getItem(int position) {
return position;
}
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
View vi=convertView;
if(convertView==null)
vi = inflater.inflate(R.layout.item, null);
TextView text=(TextView)vi.findViewById(R.id.text);;
ImageView image=(ImageView)vi.findViewById(R.id.image);
text.setText("item "+position);
imageLoader.DisplayImage(data.get(position), image);
return vi;
}
}
MainActivity.java:
package com.listviewdragginganimation;
import java.util.ArrayList;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ListView;
public class MainActivity extends Activity {
ListView list;
static LazyAdapter adapter;
static ArrayList<String>mCheeseList = new ArrayList<String>();
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
for (int i = 0; i < mStrings.length; ++i) {
mCheeseList.add(mStrings[i]);
}
list=(ListView)findViewById(R.id.list);
adapter=new LazyAdapter(this, mCheeseList);
list.setAdapter(adapter);
Button b=(Button)findViewById(R.id.button1);
b.setOnClickListener(listener);
}
#Override
public void onDestroy()
{
list.setAdapter(null);
super.onDestroy();
}
public OnClickListener listener=new OnClickListener(){
#Override
public void onClick(View arg0) {
adapter.imageLoader.clearCache();
adapter.notifyDataSetChanged();
}
};
private String[] mStrings={
"http://a3.twimg.com/profile_images/670625317/aam-logo-v3-twitter.png",
"http://a3.twimg.com/profile_images/740897825/AndroidCast-350_normal.png",
"http://a3.twimg.com/profile_images/121630227/Droid_normal.jpg",
"http://a1.twimg.com/profile_images/957149154/twitterhalf_normal.jpg",
"http://a1.twimg.com/profile_images/97470808/icon_normal.png",
"http://a3.twimg.com/profile_images/511790713/AG.png",
"http://a3.twimg.com/profile_images/956404323/androinica-avatar_normal.png",
"http://a1.twimg.com/profile_images/909231146/Android_Biz_Man_normal.png",
"http://a3.twimg.com/profile_images/72774055/AndroidHomme-LOGO_normal.jpg",
"http://a1.twimg.com/profile_images/349012784/android_logo_small_normal.jpg",
"http://a1.twimg.com/profile_images/841338368/ea-twitter-icon.png",
"http://a3.twimg.com/profile_images/64827025/android-wallpaper6_2560x160_normal.png",
"http://a3.twimg.com/profile_images/77641093/AndroidPlanet_normal.png",
};
}
I found this particular code https://github.com/bauerca/drag-sort-listview in this url and in it I was able to add images also...
I'm also trying to make a custom layout. Added an image (from a 'drawable' folder) to 'layout/text_view.xml' (inserted in a LinearLayout). Changed StableArrayAdapter to extend BaseAdapter and rewrote getView (I think you can also make this with ArrayAdapter like https://stackoverflow.com/a/30270370/2914140). Then applied some changes for Lollipop from ListViewDraggingAnimation broken on Android 5 Lollipop and https://www.youtube.com/all_comments?v=_BZIvjMgH-Q. At least, static images are dragged along with text. When I change an ImageView to a WebVew, there are many errors and bugs.
I have created a CustomView for Calendar. I have an onDraw method to draw the text in the calendar. I want to change the color of the day Text when i click on it. How can i achieve it?
CalendarView.java :
package com.example.calendar_module;
import java.util.Calendar;
import android.app.ActionBar.LayoutParams;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.MonthDisplayHelper;
import android.view.MotionEvent;
import android.widget.ImageView;
public class CalendarView extends ImageView {
private static int WEEK_TOP_MARGIN = 0;
private static int WEEK_LEFT_MARGIN = 05;
private static int CELL_WIDTH = 20;
private static int CELL_HEIGH = 20;
private static int CELL_MARGIN_TOP = 05;
private static int CELL_MARGIN_LEFT = 29;
private static float CELL_TEXT_SIZE;
private static int CALENDAR_WIDTH;
private static int CALENDAR_HEIGHT;
private static final String TAG = "CalendarView";
private String[] mDayString = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
private Calendar mRightNow = null;
private Drawable mWeekTitle = null;
private Cell mToday = null;
private Cell[][] mCells = new Cell[6][7];
private Cell[] mDayCells = new Cell[7];
private OnCellTouchListener mOnCellTouchListener = null;
MonthDisplayHelper mHelper;
Drawable mDecoration = null;
public interface OnCellTouchListener {
public void onTouch(Cell cell);
}
public CalendarView(Context context) {
this(context, null);
}
public CalendarView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CalendarView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mDecoration = context.getResources().getDrawable(R.drawable.typeb_calendar_today);
initCalendarView();
}
private void initCalendarView() {
mRightNow = Calendar.getInstance();
// prepare static vars
Resources res = getResources();
WEEK_TOP_MARGIN = (int) res.getDimension(R.dimen.week_top_margin);
WEEK_LEFT_MARGIN = (int) res.getDimension(R.dimen.week_left_margin);
CELL_WIDTH = (int) res.getDimension(R.dimen.cell_width);
CELL_HEIGH = (int) res.getDimension(R.dimen.cell_heigh);
CELL_MARGIN_TOP = (int) res.getDimension(R.dimen.cell_margin_top);
CELL_MARGIN_LEFT = (int) res.getDimension(R.dimen.cell_margin_left);
// CALENDAR_HEIGHT = 700;
// CALENDAR_WIDTH = 700;
//
CALENDAR_HEIGHT = (int) res.getDimension(R.dimen.calendar_height);
CALENDAR_WIDTH = (int) res.getDimension(R.dimen.calendar_width);
System.out.println("Width and Hight :"+CALENDAR_WIDTH+" "+CALENDAR_HEIGHT);
System.out.println("Dimen Width and Height : "+R.dimen.calendar_width+" "+R.dimen.calendar_height);
CELL_TEXT_SIZE = res.getDimension(R.dimen.cell_text_size);
// set background
// setImageResource(R.drawable.background);
mWeekTitle = res.getDrawable(R.drawable.calendar_week);
mHelper = new MonthDisplayHelper(mRightNow.get(Calendar.YEAR), mRightNow.get(Calendar.MONTH));
}
private void initCells() {
class _calendar {
public int day;
public boolean thisMonth;
public _calendar(int d, boolean b) {
day = d;
thisMonth = b;
}
public _calendar(int d) {
this(d, false);
}
};
_calendar tmp[][] = new _calendar[6][7];
for(int i=0; i<tmp.length; i++) {
int n[] = mHelper.getDigitsForRow(i);
for(int d=0; d<n.length; d++) {
if(mHelper.isWithinCurrentMonth(i,d))
tmp[i][d] = new _calendar(n[d], true);
else
tmp[i][d] = new _calendar(n[d]);
}
}
Calendar today = Calendar.getInstance();
int thisDay = 0;
mToday = null;
if(mHelper.getYear()==today.get(Calendar.YEAR) && mHelper.getMonth()==today.get(Calendar.MONTH)) {
thisDay = today.get(Calendar.DAY_OF_MONTH);
}
// build cells
Rect Bound = new Rect(CELL_MARGIN_LEFT, CELL_MARGIN_TOP, CELL_WIDTH+CELL_MARGIN_LEFT, CELL_HEIGH+CELL_MARGIN_TOP);
// for( int i=0 ; i < 7 ; i++ )
// {
//
// mDayCells[i] = new Cell(mDayString[i],new Rect(Bound),CELL_TEXT_SIZE);
// Bound.offset(CELL_WIDTH, 0);
//
// }
//
// Bound.offset(0, CELL_HEIGH); // move to next row and first column
// Bound.left = CELL_MARGIN_LEFT;
// Bound.right = CELL_MARGIN_LEFT+CELL_WIDTH;
//
for(int week=0; week<mCells.length; week++) {
for(int day=0; day<mCells[week].length; day++)
{
if(tmp[week][day].thisMonth) {
if(day==0 || day==6 )
mCells[week][day] = new RedCell(tmp[week][day].day, new Rect(Bound), CELL_TEXT_SIZE);
else
mCells[week][day] = new Cell(tmp[week][day].day, new Rect(Bound), CELL_TEXT_SIZE);
} else {
mCells[week][day] = new GrayCell(tmp[week][day].day, new Rect(Bound), CELL_TEXT_SIZE);
}
Bound.offset(CELL_WIDTH, 0); // move to next column
// get today
if(tmp[week][day].day==thisDay && tmp[week][day].thisMonth) {
mToday = mCells[week][day];
mDecoration.setBounds(mToday.getBound());
}
}
Bound.offset(0, CELL_HEIGH); // move to next row and first column
Bound.left = CELL_MARGIN_LEFT;
Bound.right = CELL_MARGIN_LEFT+CELL_WIDTH;
}
}
#Override
public void onLayout(boolean changed, int left, int top, int right, int bottom) {
// Rect re = getDrawable().getBounds();
// WEEK_LEFT_MARGIN = CELL_MARGIN_LEFT = (right-left - re.width()) / 2;
// mWeekTitle.setBounds(WEEK_LEFT_MARGIN, WEEK_TOP_MARGIN, WEEK_LEFT_MARGIN+mWeekTitle.getMinimumWidth(), WEEK_TOP_MARGIN+mWeekTitle.getMinimumHeight());
initCells();
super.onLayout(changed, left, top, right, bottom);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
System.out.println("Width : "+CALENDAR_WIDTH);
System.out.println("Height : "+CALENDAR_HEIGHT);
setMeasuredDimension(CALENDAR_WIDTH, CALENDAR_HEIGHT);
// setMeasuredDimension(100,200);
}
public void setTimeInMillis(long milliseconds) {
mRightNow.setTimeInMillis(milliseconds);
initCells();
this.invalidate();
}
public int getYear() {
return mHelper.getYear();
}
public int getMonth() {
return mHelper.getMonth();
}
public void nextMonth() {
mHelper.nextMonth();
initCells();
invalidate();
}
public void previousMonth() {
mHelper.previousMonth();
initCells();
invalidate();
}
public boolean firstDay(int day) {
return day==1;
}
public boolean lastDay(int day) {
return mHelper.getNumberOfDaysInMonth()==day;
}
public void goToday() {
Calendar cal = Calendar.getInstance();
mHelper = new MonthDisplayHelper(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH));
initCells();
invalidate();
}
public Calendar getDate() {
return mRightNow;
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if(mOnCellTouchListener!=null){
for(Cell[] week : mCells) {
for(Cell day : week) {
if(day.hitTest((int)event.getX(), (int)event.getY())) {
mOnCellTouchListener.onTouch(day);
}
}
}
}
return super.onTouchEvent(event);
}
public void setOnCellTouchListener(OnCellTouchListener p) {
mOnCellTouchListener = p;
}
#Override
protected void onDraw(Canvas canvas) {
// draw background
super.onDraw(canvas);
mWeekTitle.draw(canvas);
// draw cells
for(Cell[] week : mCells) {
for(Cell day : week) {
day.draw(canvas);
}
}
// draw today
if(mDecoration!=null && mToday!=null) {
mDecoration.draw(canvas);
}
}
public class GrayCell extends Cell {
public GrayCell(int dayOfMon, Rect rect, float s) {
super(dayOfMon, rect, s);
mPaint.setColor(Color.LTGRAY);
}
}
private class RedCell extends Cell {
public RedCell(int dayOfMon, Rect rect, float s) {
super(dayOfMon, rect, s);
mPaint.setColor(0xdddd0000);
}
}
}
Cell.java :
package com.example.calendar_module;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.util.Log;
public class Cell {
private static final String TAG = "Cell";
protected Rect mBound = null;
protected int mDayOfMonth = 1; // from 1 to 31
protected Paint mPaint = new Paint(Paint.SUBPIXEL_TEXT_FLAG
|Paint.ANTI_ALIAS_FLAG);
int dx, dy;
protected String mDayString;
private Cell cell ;
public Cell(int dayOfMon, Rect rect, float textSize, boolean bold) {
mDayOfMonth = dayOfMon;
mBound = rect;
mPaint.setTextSize(textSize);
mPaint.setColor(Color.BLACK);
if(bold) mPaint.setFakeBoldText(true);
dx = (int) mPaint.measureText(String.valueOf(mDayOfMonth)) / 2;
dy = (int) (-mPaint.ascent() + mPaint.descent()) / 2;
}
public Cell(String day,Rect rect,float textSize,boolean bold)
{
mDayString = day;
mBound = rect;
mPaint.setTextSize(textSize);
mPaint.setColor(Color.BLACK);
if(bold) mPaint.setFakeBoldText(true);
dx = (int) mPaint.measureText(String.valueOf(mDayString)) / 2;
dy = (int) (-mPaint.ascent() + mPaint.descent()) / 2;
}
public Cell(String day , Rect rect , float textSize , int newColor)
{
mDayString = day;
mBound = rect;
mPaint.setTextSize(textSize);
mPaint.setColor(newColor);
dx = (int) mPaint.measureText(String.valueOf(mDayString)) / 2;
dy = (int) (-mPaint.ascent() + mPaint.descent()) / 2;
}
public Cell(int dayOfMon, Rect rect, float textSize) {
this(dayOfMon, rect, textSize, false);
}
public Cell(String day,Rect rect,float textSize)
{
this(day,rect,textSize,false);
}
protected void draw(Canvas canvas) {
canvas.drawText(String.valueOf(mDayOfMonth), mBound.centerX() - dx, mBound.centerY() + dy, mPaint);
}
public int getDayOfMonth() {
return mDayOfMonth;
}
public boolean hitTest(int x, int y) {
return mBound.contains(x, y);
}
public Rect getBound() {
return mBound;
}
public String toString() {
return String.valueOf(mDayOfMonth)+"("+mBound.toString()+")";
}
}
CalendarActivity.java
package com.example.calendar_module;
import java.util.GregorianCalendar;
import com.example.calendar_module.CalendarView.OnCellTouchListener;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Color;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.Handler;
import android.text.format.DateUtils;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
public class CalendarActivity extends Activity{
public static final String MIME_TYPE = "vnd.android.cursor.dir/vnd.example.calendar_module.date";
public static String mClickedDate, mClickedMonth;
CalendarView mView = null;
TextView mHit;
Handler mHandler = new Handler();
private Button mNextButton;
private Button mPreviousButton;
private TextView mMonthText ;
private int mMonthInt;
private ListView mEventListView;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mView = (CalendarView)findViewById(R.id.calendar);
mEventListView = (ListView) findViewById(R.id.EventsList);
mEventListView.setAdapter(new BadgeAdapter(this));
mView.setOnCellTouchListener(new OnCellTouchListener() {
#Override
public void onTouch(Cell cell) {
TextView mMonthText = (TextView)findViewById(R.id.MonthText);
mClickedDate = ""+cell.getDayOfMonth();
mClickedMonth =""+mMonthText.getText();
// System.out.println("Clicked date is : "+mClickedDate+" "+mClickedMonth);
// startActivity(new Intent(CalendarActivity.this,Event.class));
// cell = new Cell(cell.getDayOfMonth(),new Rect(cell.getBound()),Color.MAGENTA);
}
});
mNextButton = (Button) findViewById(R.id.NextMonth);
mPreviousButton = (Button) findViewById(R.id.PreviousMonth);
mMonthText = (TextView) findViewById(R.id.MonthText);
mMonthText.setText(DateUtils.getMonthString(mView.getMonth(), DateUtils.LENGTH_LONG)+", "+mView.getYear());
// System.out.println("CurrentMonth is : "+mView.getMonth());
// if(getIntent().getAction().equals(Intent.ACTION_PICK))
// findViewById(R.id.hint).setVisibility(View.INVISIBLE);
mNextButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
mView.nextMonth();
mMonthText.setText(DateUtils.getMonthString(mView.getMonth(), DateUtils.LENGTH_LONG)+", "+mView.getYear());
}
});
mPreviousButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
mView.previousMonth();
mMonthText.setText(DateUtils.getMonthString(mView.getMonth(), DateUtils.LENGTH_LONG)+", "+mView.getYear());
}
});
System.out.println("Calendar View Height : "+mView.getHeight());
}
private class RedCell extends Cell {
public RedCell(Cell cell) {
super(cell.getDayOfMonth(),new Rect(cell.getBound()), R.dimen.cell_text_size);
mPaint.setColor(Color.RED);
}
}
}
Call mView.invalidate(); in your onClick() method for that View. This will call onDraw() (eventually), which will then run the drawing code for your view.
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;
}
}
I am a beginner Android developer and I am trying to make a game where you move a bat (catcher) along the bottom of the screen and you have to catch falling blocks.
I am struggling currently with the collision detection. The collision detection itself is working correctly but when I then try to remove the block that is falling the game crashes.
The problem code is the line under the "remove block" comment in the checkCollisions() method but I have posted the whole GameView below for clarity.
Here is the code:
package com.mattdrewery.supercatch;
import android.view.View;
import android.view.MotionEvent;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Rect;
import java.util.Random;
import java.util.ArrayList;
public class GameView extends View
{
private Catcher catcher;
private ArrayList<NormalBlock> blocks;
private Random rand;
private int screenWidth;
private int screenHeight;
private boolean gameOver;
private int updateCount;
public GameView(Context context)
{
super(context);
setFocusable(true);
updateCount = 1;
// Set gameOver to false
gameOver = false;
// Create the ArrayList of normal blocks
blocks = new ArrayList<NormalBlock>();
// Create a random number generator
rand = new Random();
// Create the catcher
catcher = new Catcher(context, R.drawable.catcher, 230, 250);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh)
{
screenWidth = this.getWidth();
screenHeight = this.getHeight();
}
#Override
protected void onDraw(Canvas canvas)
{
updateCatcher();
checkCollisions();
updateBlocks();
// Draw the catcher to the canvas
canvas.drawBitmap(catcher.getImage(), catcher.getPosX(), catcher.getPosY(), null);
// For each normal block in the block array
for (NormalBlock block : blocks)
{
// Draw block to the canvas
canvas.drawBitmap(block.getImage(), block.getPosX(), block.getPosY(), null);
}
// Redraw the screen
invalidate();
}
#Override
public boolean onTouchEvent(MotionEvent event)
{
// Get the action from the touch screen
int eventAction = event.getAction();
int X = (int) event.getX();
// If the user presses on the screen....
if (eventAction == MotionEvent.ACTION_DOWN)
{
if (X < screenWidth / 2)
{
catcher.setMovingLeft(true);
catcher.setMovingRight(false);
}
else
{
catcher.setMovingLeft(false);
catcher.setMovingRight(true);
}
}
if (eventAction == MotionEvent.ACTION_MOVE)
{
if (X < screenWidth / 2)
{
catcher.setMovingLeft(true);
catcher.setMovingRight(false);
}
else
{
catcher.setMovingLeft(false);
catcher.setMovingRight(true);
}
}
if (eventAction == MotionEvent.ACTION_UP)
{
catcher.setMovingLeft(false);
catcher.setMovingRight(false);
}
return true;
}
private void updateCatcher()
{
// Check whether the catcher is moving and update position accordingly
if (catcher.isMovingLeft())
{
catcher.moveLeft();
if (catcher.getPosX() < 0)
{
catcher.setPosX(0);
}
}
else if (catcher.isMovingRight())
{
catcher.moveRight();
if (catcher.getPosX() > (screenWidth - catcher.getImage().getWidth()))
{
catcher.setPosX(screenWidth - catcher.getImage().getWidth());
}
}
}
private void updateBlocks()
{
updateCount++;
if (updateCount >= 100)
{
updateCount = 1;
blocks.add(new NormalBlock(getContext(), R.drawable.nblock,
rand.nextInt(screenWidth - 20), 0));
}
// For each normal block in the block array
for (NormalBlock block : blocks)
{
// Check if block has hit the bottom of the screen
if (block.getPosY() + block.getImage().getHeight() > screenHeight)
{
gameOver = true;
}
else
{
// Drop the block down
block.dropDown();
}
}
}
private void checkCollisions()
{
// Get Rectangle for the catcher
Rect catcherRect = new Rect(catcher.posX, catcher.posY,
catcher.posX + catcher.image.getWidth(),
catcher.posY + catcher.image.getHeight());
// For each block in the array
for (NormalBlock block : blocks)
{
// Get Rectangle for the current block
Rect blockRect = new Rect(block.posX, block.posY,
block.posX + block.image.getWidth(),
block.posY + block.image.getHeight());
if (Rect.intersects(catcherRect, blockRect))
{
// Remove block
blocks.remove(block);
}
}
}
public int getScreenHeight()
{
return screenHeight;
}
public int getScreenWidth()
{
return screenWidth;
}
}
Thanks in advance,
Try replacing for (NormalBlock block : blocks) with this:
int count = blocks.size();
for (int i=0;i<count;i++){
Block block = blocks.get(i);
I'm guessing you were getting a ConcurrentModificationException there.