Basically I am working on a custom keyboard in that I have a suggestion bar connected with my keyboard but the problem is that suggestion bar is overlapping the edit_text/inputview in any application, I also attached the screenshot so u will better understandHere is the screenshot
Here my codes for this suggestion bar
`public class CandidateView extends View {
private static final int OUT_OF_BOUNDS = -1;
private SoftKeyboard mService;
private List<String> mSuggestions;
private int mSelectedIndex;
private int mTouchX = OUT_OF_BOUNDS;
private final Drawable mSelectionHighlight;
private boolean mTypedWordValid;
private Rect mBgPadding;
private static final int MAX_SUGGESTIONS = 4;
private static final int SCROLL_PIXELS = 40;
private final int[] mWordWidth = new int[MAX_SUGGESTIONS];
private final int[] mWordX = new int[MAX_SUGGESTIONS];
private static final int X_GAP = 95;
private static final List<String> EMPTY_LIST = new ArrayList<String>();
private final int mColorNormal;
private final int mColorRecommended;
private final int mColorOther;
private final int mVerticalPadding;
private int mhorizentmargin;
private final Paint mPaint;
private boolean mScrolled;
private int mTargetScrollX;
private int mTotalWidth;
private final int extraHeight = 1;
private final GestureDetector mGestureDetector;
/**
* Construct a CandidateView for showing suggested words for completion.
*
* #param context
*/
public CandidateView(Context context) {
super(context);
mSelectionHighlight = context.getResources().getDrawable(
R.drawable.kb_bg_4);
mSelectionHighlight.setState(new int[]{
android.R.attr.state_enabled, android.R.attr.state_focused,
android.R.attr.state_window_focused,android.R.attr.state_pressed
});
// background color of hints
Resources r = context.getResources();
setBackgroundColor(r.getColor(R.color.candidate_background));
mColorNormal = r.getColor(R.color.candidate_normal);
mColorRecommended = r.getColor(R.color.candidate_recommended);
mColorOther = r.getColor(R.color.candidateViewColor);
mVerticalPadding = r.getDimensionPixelSize(R.dimen.candidate_vertical_padding);
mPaint = new Paint();
mPaint.setColor(mColorNormal);
mPaint.setAntiAlias(true);
mPaint.setTextSize(r.getDimensionPixelSize(R.dimen.candidate_font_height));
mPaint.setStrokeWidth(0);
mGestureDetector = new GestureDetector(new GestureDetector.SimpleOnGestureListener() {
#Override
public boolean onScroll(MotionEvent e1, MotionEvent e2,
float distanceX, float distanceY) {
mScrolled = true;
int sx = getScrollX();
sx += distanceX;
if (sx < 0) {
sx = 0;
}
if (sx + getWidth() > mTotalWidth) {
sx -= distanceX;
}
mTargetScrollX = sx;
scrollTo(sx, getScrollY());
invalidate();
return true;
}
});
setHorizontalFadingEdgeEnabled(true);
setWillNotDraw(false);
setHorizontalScrollBarEnabled(false);
setVerticalScrollBarEnabled(false);
}
/**
* A connection back to the service to communicate with the text field
*
* #param listener
*/
public void setService(SoftKeyboard listener) {
mService = listener;
}
#Override
public int computeHorizontalScrollRange() {
return mTotalWidth;
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int measuredWidth = resolveSize(50, widthMeasureSpec);
// Get the desired height of the icon menu view (last row of items does
// not have a divider below)
Rect padding = new Rect();
mSelectionHighlight.getPadding(padding);
final int desiredHeight = ((int) mPaint.getTextSize()) + mVerticalPadding
;
// Maximum possible width and desired height
setMeasuredDimension(measuredWidth,
resolveSize(desiredHeight, heightMeasureSpec));
}
/**
* If the canvas is null, then only touch calculations are performed to pick the target
* candidate.
*/
#Override
protected void onDraw(Canvas canvas) {
if (canvas != null) {
super.onDraw(canvas);
}
mTotalWidth = 0;
if (mSuggestions == null) return;
if (mBgPadding == null) {
mBgPadding = new Rect(0, 0, 0, 0);
if (getBackground() != null) {
getBackground().getPadding(mBgPadding);
}
}
int x = 0;
final int count = mSuggestions.size();
final int height = getHeight();
final Rect bgPadding = mBgPadding;
final Paint paint = mPaint;
final int touchX = mTouchX;
final int scrollX = getScrollX();
final boolean scrolled = mScrolled;
final boolean typedWordValid = mTypedWordValid;
final int y = (int) (((height - mPaint.getTextSize()) / 2) - mPaint.ascent());
for (int i = 0; i < count; i++) {
// Break the loop. This fix the app from crashing.
if(i >= MAX_SUGGESTIONS){
break;
}
String suggestion = mSuggestions.get(i);
float textWidth = paint.measureText(suggestion);
final int wordWidth = (int) textWidth + X_GAP * 2;
mWordX[i] = x;
mWordWidth[i] = wordWidth;
paint.setColor(mColorNormal);
if (touchX + scrollX >= x && touchX + scrollX < x + wordWidth && !scrolled) {
if (canvas != null) {
canvas.translate(x, 0);
mSelectionHighlight.setBounds(0, bgPadding.top, wordWidth, height);
mSelectionHighlight.draw(canvas);
canvas.translate(-x, 0);
}
mSelectedIndex = i;
}
if (canvas != null) {
if ((i == 1 && !typedWordValid) || (i == 0 && typedWordValid)) {
paint.setFakeBoldText(true);
paint.setColor(mColorRecommended);
} else if (i != 0) {
paint.setColor(mColorOther);
}
canvas.drawText(suggestion, x + X_GAP, y, paint);
paint.setColor(mColorOther);
canvas.drawLine(x + wordWidth + 0.5f, bgPadding.top,
x + wordWidth + 0.5f, height + 10, paint);
paint.setFakeBoldText(false);
}
x += wordWidth;
}
mTotalWidth = x;
if (mTargetScrollX != getScrollX()) {
scrollToTarget();
}
}
private void scrollToTarget() {
int sx = getScrollX();
if (mTargetScrollX > sx) {
sx += SCROLL_PIXELS;
if (sx >= mTargetScrollX) {
sx = mTargetScrollX;
requestLayout();
}
} else {
sx -= SCROLL_PIXELS;
if (sx <= mTargetScrollX) {
sx = mTargetScrollX;
requestLayout();
}
}
scrollTo(sx, getScrollY());
invalidate();
}
#SuppressLint("WrongCall")
public void setSuggestions(List<String> suggestions, boolean completions,
boolean typedWordValid) {
clear();
if (suggestions != null) {
mSuggestions = new ArrayList<String>(suggestions);
}
mTypedWordValid = typedWordValid;
scrollTo(0, 0);
mTargetScrollX = 0;
// Compute the total width
onDraw(null);
invalidate();
requestLayout();
}
public void clear() {
mSuggestions = EMPTY_LIST;
mTouchX = OUT_OF_BOUNDS;
mSelectedIndex = -1;
invalidate();
}
#Override
public boolean onTouchEvent(MotionEvent me) {
if (mGestureDetector.onTouchEvent(me)) {
return true;
}
int action = me.getAction();
int x = (int) me.getX();
int y = (int) me.getY();
mTouchX = x;
switch (action) {
case MotionEvent.ACTION_DOWN:
mScrolled = false;
invalidate();
break;
case MotionEvent.ACTION_MOVE:
if (y <= 0) {
// Fling up!?
if (mSelectedIndex >= 0) {
mService.pickSuggestionManually(mSelectedIndex);
mSelectedIndex = -1;
}
}
invalidate();
break;
case MotionEvent.ACTION_UP:
if (!mScrolled) {
if (mSelectedIndex >= 0) {
mService.pickSuggestionManually(mSelectedIndex);
}
}
mSelectedIndex = -1;
removeHighlight();
requestLayout();
break;
}
return true;
}
/**
* For flick through from keyboard, call this method with the x coordinate of the flick
* gesture.
*
* #param x
*/
#SuppressLint("WrongCall")
public void takeSuggestionAt(float x) {
mTouchX = (int) x;
// To detect candidate
onDraw(null);
if (mSelectedIndex >= 0) {
mService.pickSuggestionManually(mSelectedIndex);
}
invalidate();
}
private void removeHighlight() {
mTouchX = OUT_OF_BOUNDS;
invalidate();
}
}`
If your application is fullscreen then use fitsSystemWindows as true;
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
else use windowSoftInputMode as adjustResize in AndroidManifest.xml.
<activity
android:name=".activity.MainActivity"
android:windowSoftInputMode="adjustResize|stateHidden" />
Related
I'm developing a game, and I have a custom view Joystick which haves a onTouch event. I also have a ImageView which also haves onTouch for shooting.
If I add the views programatically using java code to a main relative layout, the onTouch of the two views works perfectly and at the same time.
The problem is that if I add the views using a XML layout file, then, if you touch the custom view Joystick, the onTouch of the other view doesn't works. It only happens if I use XML layout file for creating the layout.
How can I solve the problem?
The layout:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/mainLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="#+id/shootImageView"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:src="#drawable/shoot"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true" />
<com.myapp.uiviews.Joystick
android:id="#+id/joystick"
android:layout_width="wrap_content"
android:layout_height="150dp"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true" />
</RelativeLayout>
The class:
mainLayout = (RelativeLayout) findViewById(R.id.mainLayout);
shootImageView= (ImageView) findViewById(R.id.shootImageView);
shootImageView.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
touchX=sw/2;
touchY=sh/2;
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
LaserManager.getInstance().startLaserThread();
break;
case MotionEvent.ACTION_UP:
LaserManager.getInstance().stopLaserThread();
break;
}
return true;
}
});
shootImageView.invalidate();
shootImageView.refreshDrawableState();
shootImageView.forceLayout();
joystickOnScreen = (Joystick) findViewById(R.id.joystick);
int factor = GameState.getInstance().getSpaceship().getSpeedFactorCorrespondence();
joystickOnScreen.setMovementRange(sh/factor);
joystickOnScreen.setInnerPadding(sh/30);
joystickOnScreen.setOnJostickMovedListener(joystickListener);
joystickOnScreen.invalidate();
joystickOnScreen.refreshDrawableState();
joystickOnScreen.forceLayout();
Using that code doesn't works, but if I use this next code it works perfectly... why?
shootImageView = new ImageView(this);
shootImageView.setId(102);
RelativeLayout.LayoutParams shootImageViewParams = new RelativeLayout.LayoutParams(sw/8, sw/8);
shootImageViewParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
shootImageViewParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
shootImageViewParams.setMargins(0, 0, sh/15, sh/15);
shootImageView.setLayoutParams(shootImageViewParams);
shootImageView.setImageDrawable(getResources().getDrawable(R.drawable.shoot));
shootImageView.setAlpha(0.4f);
shootImageView.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
touchX=sw/2;
touchY=sh/2;
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
LaserManager.getInstance().startLaserThread();
break;
case MotionEvent.ACTION_UP:
LaserManager.getInstance().stopLaserThread();
break;
}
return true;
}
});
main.addView(shootImageView);
joystickOnScreen = new Joystick(this);
int factor = GameState.getInstance().getSpaceship().getSpeedFactorCorrespondence();
joystickOnScreen.setMovementRange(sh/factor);
joystickOnScreen.setInnerPadding(sh/30);
joystickOnScreen.setOnJostickMovedListener(joystickListener);
RelativeLayout.LayoutParams joystickParams = new RelativeLayout.LayoutParams(sh/3, sh/3);
joystickParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
joystickParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
joystickParams.setMargins(sh/100, 0, 0, sh/100);
joystickOnScreen.setLayoutParams(joystickParams);
joystickOnScreen.setAlpha(0.3f);
main.addView(joystickOnScreen);
This is the Joystick class:
public class Joystick extends View {
public static final int INVALID_POINTER = -1;
private JoystickMovedListener moveListener;
//# of pixels moveWithAcceleration required between reporting to the listener
private float moveResolution;
//Max range of moveWithAcceleration in user coordinate system
private float movementRange;
//Last touch point in view coordinates
private int pointerId = INVALID_POINTER;
private float touchX;
private float touchY;
private float touchXDelayedMovement;
private float touchYDelayedMovement;
//Handle center in view coordinates
private float handleX;
private float handleY;
//Last reported position in view coordinates (allows different reporting sensitivities)
private float reportX;
private float reportY;
//Center of the view in view coordinates
private int cX;
private int cY;
//Size of the view in view coordinates
private int dimX;
private int dimY;
private int innerPadding;
private int bgRadius;
private int handleRadius;
private int movementRadius;
private int handleInnerBoundaries;
//Cartesian coordinates of last touch point - joystick center is (0,0)
private int cartX;
private int cartY;
//User coordinates of last touch point
private int userX;
private int userY;
//Offset co-ordinates (used when touch events are received from parent's coordinate origin)
private int offsetX;
private int offsetY;
private Paint bgPaint;
private Paint handlePaint;
boolean disabled;
Handler handler;
Handler handlerDelayedMovement;
public Joystick(Context context, AttributeSet attrs) {
super(context, attrs);
initJoystickView();
}
public Joystick(Context context) {
super(context);
initJoystickView();
}
private void initJoystickView() {
setFocusable(true);
handlePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
handlePaint.setColor(Color.RED);
handlePaint.setStrokeWidth(1);
handlePaint.setStyle(Paint.Style.FILL_AND_STROKE);
bgPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
bgPaint.setColor(Color.DKGRAY);
bgPaint.setStrokeWidth(1);
bgPaint.setStyle(Paint.Style.FILL_AND_STROKE);
this.moveResolution = 1.0f;
handler = new Handler();
handlerDelayedMovement = new Handler();
}
public void setMovementRange(float movementRange) {
this.movementRange = movementRange;
}
public void setOnJostickMovedListener(JoystickMovedListener listener) {
this.moveListener = listener;
}
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
int d = Math.min(getMeasuredWidth(), getMeasuredHeight());
dimX = d;
dimY = d;
cX = d / 2;
cY = d / 2;
bgRadius = dimX/2 - innerPadding;
handleRadius = (int)(d * 0.2);
handleInnerBoundaries = handleRadius;
movementRadius = Math.min(cX, cY) - handleInnerBoundaries;
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// Here we make sure that we have a perfect circle
int measuredWidth = measure(widthMeasureSpec);
int measuredHeight = measure(heightMeasureSpec);
setMeasuredDimension(measuredWidth, measuredHeight);
}
private int measure(int measureSpec) {
int result = 0;
// Decode the measurement specifications.
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
if (specMode == MeasureSpec.UNSPECIFIED) {
result = 200; // Return a default size of 200 if no bounds are specified.
} else {
result = specSize; // As you want to fill the available space always return the full available bounds.
}
return result;
}
#Override
protected void onDraw(Canvas canvas) {
canvas.save();
// Draw the background
canvas.drawCircle(cX, cY, bgRadius, bgPaint);
// Draw the handle
handleX = touchX + cX;
handleY = touchY + cY;
canvas.drawCircle(handleX, handleY, handleRadius, handlePaint);
canvas.restore();
}
public void setPointerId(int id) {
this.pointerId = id;
}
public int getPointerId() {
return pointerId;
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
final int action = ev.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_MOVE: {
if (disabled==true)
break;
return processMoveEvent(ev);
}
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP: {
if ( pointerId != INVALID_POINTER ) {
returnHandleToCenterVisually();
returnHandleToCenterAcceleratedMovement();
setPointerId(INVALID_POINTER);
}
break;
}
case MotionEvent.ACTION_POINTER_UP: {
if ( pointerId != INVALID_POINTER ) {
final int pointerIndex = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
final int pointerId = ev.getPointerId(pointerIndex);
if ( pointerId == this.pointerId ) {
returnHandleToCenterVisually();
returnHandleToCenterAcceleratedMovement();
setPointerId(INVALID_POINTER);
return true;
}
}
break;
}
case MotionEvent.ACTION_DOWN: {
handlerDelayedMovement.removeCallbacksAndMessages(null);
if ( pointerId == INVALID_POINTER ) {
int x = (int) ev.getX();
if ( x >= offsetX && x < offsetX + dimX ) {
setPointerId(ev.getPointerId(0));
if (disabled==true){
return true;
}
return processMoveEvent(ev);
}
}
break;
}
case MotionEvent.ACTION_POINTER_DOWN: {
if ( pointerId == INVALID_POINTER ) {
final int pointerIndex = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
final int pointerId = ev.getPointerId(pointerIndex);
int x = (int) ev.getX(pointerId);
if ( x >= offsetX && x < offsetX + dimX ) {
setPointerId(pointerId);
return true;
}
}
break;
}
}
return false;
}
private boolean processMoveEvent(MotionEvent ev) {
if ( pointerId != INVALID_POINTER ) {
final int pointerIndex = ev.findPointerIndex(pointerId);
// Translate touch position to center of view
float x = ev.getX(pointerIndex);
touchX = x - cX - offsetX;
float y = ev.getY(pointerIndex);
touchY = y - cY - offsetY;
moveWithAcceleration();
invalidate();
return true;
}
return false;
}
private void moveWithAcceleration() {
float diffX = touchX;
float diffY = touchY;
double radial = Math.sqrt((diffX*diffX) + (diffY*diffY));
if ( radial > movementRadius ) {
touchX = (int)((diffX / radial) * movementRadius);
touchY = (int)((diffY / radial) * movementRadius);
}
final int numberOfFrames = 5;
final double intervalsX = (touchX - touchXDelayedMovement) / numberOfFrames;
final double intervalsY = (touchY - touchYDelayedMovement) / numberOfFrames;
handlerDelayedMovement.removeCallbacksAndMessages(null);
for (int i = 0; i <= numberOfFrames; i++) {
handlerDelayedMovement.postDelayed(new Runnable() {
#Override
public void run() {
touchXDelayedMovement += intervalsX;
touchYDelayedMovement += intervalsY;
reportOnMoved();
}
}, i * 50);
}
}
private void reportOnMoved() {
//We calc user coordinates
//First convert to cartesian coordinates
cartX = (int)(touchXDelayedMovement / movementRadius * movementRange);
cartY = (int)(touchYDelayedMovement / movementRadius * movementRange);
//Cartesian Coordinates
userX = cartX;
userY = cartY;
if (moveListener != null) {
boolean rx = Math.abs(touchXDelayedMovement - reportX) >= moveResolution;
boolean ry = Math.abs(touchYDelayedMovement - reportY) >= moveResolution;
if (rx || ry) {
this.reportX = touchXDelayedMovement;
this.reportY = touchYDelayedMovement;
moveListener.OnMoved(userX, userY);
}
}
}
private void returnHandleToCenterVisually() {
final int numberOfFrames = 2;
final double intervalsX = (0 - touchX) / numberOfFrames;
final double intervalsY = (0 - touchY) / numberOfFrames;
handler.removeCallbacksAndMessages(null);
for (int i = 0; i < numberOfFrames; i++) {
final int j = i;
handler.postDelayed(new Runnable() {
#Override
public void run() {
touchX += intervalsX;
touchY += intervalsY;
invalidate();
if (moveListener != null && j == numberOfFrames - 1) {
moveListener.OnReturnedToCenter();
}
}
}, i * 15);
}
if (moveListener != null) {
moveListener.OnReleased();
}
}
private void returnHandleToCenterAcceleratedMovement() {
final int numberOfFrames = 10;
final double intervalsX = (0 - touchXDelayedMovement) / numberOfFrames;
final double intervalsY = (0 - touchYDelayedMovement) / numberOfFrames;
handlerDelayedMovement.removeCallbacksAndMessages(null);
for (int i = 0; i < numberOfFrames; i++) {
handlerDelayedMovement.postDelayed(new Runnable() {
#Override
public void run() {
touchXDelayedMovement += intervalsX;
touchYDelayedMovement += intervalsY;
reportOnMoved();
}
}, i * 50);
}
}
public void setInnerPadding(int innerPadding){
this.innerPadding=innerPadding;
}
public void disable(){
disabled=true;
}
public void enable(){
disabled=false;
}
public interface JoystickMovedListener {
public void OnMoved(int pan, int tilt);
public void OnReleased();
public void OnReturnedToCenter();
}
}
Incredible but true, the problem was with
android:layout_height="wrap_content" in shoot button and android:layout_width="wrap_content" in joystick, after setting a fixed size the problem dissapeared. Didn't understand why..
I've successfully implemented a customized scroll view group to use gestures to swipe between my different fragments. But i want to sometimes disable swiping to next fragment, while still allowing going back to previous fragment. How can i do that based on the code? Thank you very much.
I've created a boolean value to indicate this nameing "allowNext"
import java.util.Vector;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.widget.Scroller;
public class HorizontalScrollInViewGroup extends ViewGroup {
private boolean allowNext = true;
private Scroller mScroller;
private VelocityTracker mVelocityTracker;
private int mCurScreen;
private int mDefaultScreen = 0;
private static final int TOUCH_STATE_REST = 0;
private static final int TOUCH_STATE_SCROLLING = 1;
private static final int SNAP_VELOCITY = 500;
private int mTouchState = TOUCH_STATE_REST;
private int mTouchSlop;
private float mLastMotionX;
private int sensitivity = 30;
private boolean spring;
private Vector<HorizontalScrollInViewGroupListener> listeners;
//used for the screen changed listener
private OnScreenChangedListener screenChangedCallBack;
public HorizontalScrollInViewGroup(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
mScroller = new Scroller(context);
setCurScreen(mDefaultScreen);
mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
listeners = new Vector<HorizontalScrollInViewGroupListener>();
//initiate the screen change listener
screenChangedCallBack = (OnScreenChangedListener)context;
}
// An interface that tells the activity the screen is switched
public interface OnScreenChangedListener {
public void onScreenChanged(int currentScreen);
}
/* (non-Javadoc)
* #see android.view.ViewGroup#onLayout(boolean, int, int, int, int)
*/
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
// TODO Auto-generated method stub
int childLeft = 0;
final int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
final View childView = getChildAt(i);
if (childView.getVisibility() != View.GONE) {
final int childWidth = childView.getMeasuredWidth();
childView.layout(childLeft, 0, childLeft + childWidth,
childView.getMeasuredHeight());
childLeft += childWidth;
}
}
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
final int width = MeasureSpec.getSize(widthMeasureSpec);
final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
if (widthMode != MeasureSpec.EXACTLY) {
throw new IllegalStateException(
"HorizontalScrollInViewGroup only canmCurScreen run at EXACTLY mode!");
}
final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
if (heightMode != MeasureSpec.EXACTLY) {
throw new IllegalStateException(
"HorizontalScrollInViewGroup only can run at EXACTLY mode!");
}
// The children are given the same width and height as the scrollLayout
final int count = getChildCount();
for (int i = 0; i < count; i++) {
getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);
}
scrollTo(getCurScreen() * width, 0);
}
#Override
public void computeScroll() {
// TODO Auto-generated method stub
if (mScroller.computeScrollOffset()) {
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
postInvalidate();
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
// TODO Auto-generated method stub
if (mVelocityTracker == null)
mVelocityTracker = VelocityTracker.obtain();
mVelocityTracker.addMovement(event);
final int action = event.getAction();
final float x = event.getX();
switch (action) {
case MotionEvent.ACTION_DOWN:
if (!mScroller.isFinished())
mScroller.abortAnimation();
mLastMotionX = x;
break;
case MotionEvent.ACTION_MOVE:
int deltaX = (int) (mLastMotionX - x);
if (Math.abs(deltaX) > sensitivity) {
if (spring) {
scrollBy(deltaX, 0);
mLastMotionX = x;
} else {
final int childCount = getChildCount();
boolean max = getCurScreen() < childCount - 1;
boolean min = getCurScreen() > 0;
boolean canMove = deltaX > 0 ? (max ? true : false)
: (min ? true : false);
if (canMove) {
scrollBy(deltaX, 0);
mLastMotionX = x;
}
}
}
break;
case MotionEvent.ACTION_UP:
final VelocityTracker velocityTracker = mVelocityTracker;
velocityTracker.computeCurrentVelocity(1000);
int velocityX = (int) velocityTracker.getXVelocity();
if (velocityX > SNAP_VELOCITY && getCurScreen() > 0) {
// Fling enough to move left
snapToScreen(getCurScreen() - 1);
} else if (velocityX < -SNAP_VELOCITY && getCurScreen() < getChildCount() - 1) {
// Fling enough to move right
snapToScreen(getCurScreen() + 1);
} else {
snapToDestination();
}
if (mVelocityTracker != null) {
mVelocityTracker.recycle();
mVelocityTracker = null;
}
mTouchState = TOUCH_STATE_REST;
break;
case MotionEvent.ACTION_CANCEL:
mTouchState = TOUCH_STATE_REST;
break;
}
return true;
}
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
// TODO Auto-generated method stub
final int action = ev.getAction();
if ((action == MotionEvent.ACTION_MOVE)
&& (mTouchState != TOUCH_STATE_REST))
return true;
final float x = ev.getX();
switch (action) {
case MotionEvent.ACTION_MOVE:
final int xDiff = (int) Math.abs(mLastMotionX - x);
if (xDiff > mTouchSlop)
mTouchState = TOUCH_STATE_SCROLLING;
break;
case MotionEvent.ACTION_DOWN:
mLastMotionX = x;
mTouchState = mScroller.isFinished() ? TOUCH_STATE_REST
: TOUCH_STATE_SCROLLING;
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
mTouchState = TOUCH_STATE_REST;
break;
}
return mTouchState != TOUCH_STATE_REST;
}
public void snapToDestination() {
final int screenWidth = getWidth();
final int destScreen = (getScrollX() + screenWidth / 2) / screenWidth;
snapToScreen(destScreen);
//call back to let the activity know the current screen
screenChangedCallBack.onScreenChanged(getCurScreen());
}
public void snapToScreen(int whichScreen) {
// get the valid layout page
int lastIndex = getCurScreen();
whichScreen = Math.max(0, Math.min(whichScreen, getChildCount() - 1));
if (getScrollX() != (whichScreen * getWidth())) {
final int delta = whichScreen * getWidth() - getScrollX();
mScroller.startScroll(getScrollX(), 0, delta, 0,
Math.abs(delta) * 2);
setCurScreen(whichScreen);
invalidate(); // Redraw the layout
}
for (HorizontalScrollInViewGroupListener listener : listeners)
listener.turnPage(lastIndex, whichScreen);
//call back to let the activity know the current screen
screenChangedCallBack.onScreenChanged(getCurScreen());
}
public void setToScreen(int whichScreen) {
whichScreen = Math.max(0, Math.min(whichScreen, getChildCount() - 1));
setCurScreen(whichScreen);
scrollTo(whichScreen * getWidth(), 0);
//call back to let the activity know the current screen
screenChangedCallBack.onScreenChanged(getCurScreen());
}
public int getCurScreen() {
return mCurScreen;
}
public void setCurScreen(int mCurScreen) {
this.mCurScreen = mCurScreen;
}
public boolean isAllowNext() {
return allowNext;
}
public void setAllowNext(boolean allowNext) {
this.allowNext = allowNext;
}
}
I have find some guy's code, a scrollLayout that it's a view group can hold some views,and you can swipe the views in it like android's view pager.for example, if i have 3 views in it. v0,v1,v2. when when I swiped the scrollLayout, it's onViewChange(View v) invoked, the v is what view you in. but now I want loop the views in it,and how Can I do it? my demand is,
when I continue swipe it,the result is ,v0,v1,v2 v0,v1,v2,v0,v1,v2...
public class ScrollLayout extends ViewGroup {
private static final String TAG = "ScrollLayout";
private Scroller mScroller;
private VelocityTracker mVelocityTracker;
private int mCurScreen;
private int mDefaultScreen = 0;
private static final int TOUCH_STATE_REST = 0;
private static final int TOUCH_STATE_SCROLLING = 1;
private static final int SNAP_VELOCITY = 9600;
private int mTouchState = TOUCH_STATE_REST;
private int mTouchSlop;
private float mLastMotionX;
private float mLastMotionY;
private OnViewChangeListener mOnViewChangeListener;
/**
* 设置是否可左右滑动
* #author liux
*/
private boolean isScroll = true;
public void setIsScroll(boolean b) {
this.isScroll = b;
}
public ScrollLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ScrollLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mScroller = new Scroller(context);
mCurScreen = mDefaultScreen;
mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int childLeft = 0;
final int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
final View childView = getChildAt(i);
if (childView.getVisibility() != View.GONE) {
final int childWidth = childView.getMeasuredWidth();
childView.layout(childLeft, 0, childLeft + childWidth,
childView.getMeasuredHeight());
childLeft += childWidth;
}
}
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//Log.e(TAG, "onMeasure");
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
final int width = MeasureSpec.getSize(widthMeasureSpec);
final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
if (widthMode != MeasureSpec.EXACTLY) {
throw new IllegalStateException(
"ScrollLayout only canmCurScreen run at EXACTLY mode!");
}
final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
if (heightMode != MeasureSpec.EXACTLY) {
throw new IllegalStateException(
"ScrollLayout only can run at EXACTLY mode!");
}
// The children are given the same width and height as the scrollLayout
final int count = getChildCount();
for (int i = 0; i < count; i++) {
getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);
}
// Log.e(TAG, "moving to screen "+mCurScreen);
scrollTo(mCurScreen * width, 0);
}
/**
* According to the position of current layout scroll to the destination
* page.
*/
public void snapToDestination() {
final int screenWidth = getWidth();
final int destScreen = (getScrollX() + screenWidth / 2) / screenWidth;
snapToScreen(destScreen);
}
public void snapToScreen(int whichScreen) {
//是否可滑动
if(!isScroll) {
this.setToScreen(whichScreen);
return;
}
scrollToScreen(whichScreen);
}
public void scrollToScreen(int whichScreen) {
// get the valid layout page
whichScreen = Math.max(0, Math.min(whichScreen, getChildCount() - 1));
if (getScrollX() != (whichScreen * getWidth())) {
final int delta = whichScreen * getWidth() - getScrollX();
mScroller.startScroll(getScrollX(), 0, delta, 0,
Math.abs(delta) * 1);//持续滚动时间 以毫秒为单位
mCurScreen = whichScreen;
invalidate(); // Redraw the layout
if (mOnViewChangeListener != null)
{
mOnViewChangeListener.OnViewChange(mCurScreen);
}
}
}
public void setToScreen(int whichScreen) {
whichScreen = Math.max(0, Math.min(whichScreen, getChildCount() - 1));
mCurScreen = whichScreen;
scrollTo(whichScreen * getWidth(), 0);
if (mOnViewChangeListener != null)
{
mOnViewChangeListener.OnViewChange(mCurScreen);
}
}
public int getCurScreen() {
return mCurScreen;
}
#Override
public void computeScroll() {
if (mScroller.computeScrollOffset()) {
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
postInvalidate();
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
//是否可滑动
if(!isScroll) {
return false;
}
Log.d("lightmanTouch", "Scroll onTouch "+event.getAction());
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
mVelocityTracker.addMovement(event);
final int action = event.getAction();
final float x = event.getX();
final float y = event.getY();
switch (action) {
case MotionEvent.ACTION_DOWN:
//Log.e(TAG, "event down!");
if (!mScroller.isFinished()) {
mScroller.abortAnimation();
}
mLastMotionX = x;
//---------------New Code----------------------
mLastMotionY = y;
//---------------------------------------------
break;
case MotionEvent.ACTION_MOVE:
int deltaX = (int) (mLastMotionX - x);
//---------------New Code----------------------
int deltaY = (int) (mLastMotionY - y);
if(Math.abs(deltaX) < 200 && Math.abs(deltaY) > 50)
break;
mLastMotionY = y;
//-------------------------------------
mLastMotionX = x;
if(Math.abs(deltaX) > 10)
scrollBy(deltaX, 0);
Log.d("movescroll", "deltax = "+ deltaX);
break;
case MotionEvent.ACTION_UP:
//Log.e(TAG, "event : up");
// if (mTouchState == TOUCH_STATE_SCROLLING) {
final VelocityTracker velocityTracker = mVelocityTracker;
velocityTracker.computeCurrentVelocity(1000);
int velocityX = (int) velocityTracker.getXVelocity();
//Log.e(TAG, "velocityX:" + velocityX);
if (velocityX > SNAP_VELOCITY && mCurScreen > 0) {
// Fling enough to move left
//Log.e(TAG, "snap left");
snapToScreen(mCurScreen - 1);
} else if (velocityX < -SNAP_VELOCITY
&& mCurScreen < getChildCount() - 1) {
// Fling enough to move right
//Log.e(TAG, "snap right");
snapToScreen(mCurScreen + 1);
} else {
snapToDestination();
}
if (mVelocityTracker != null) {
mVelocityTracker.recycle();
mVelocityTracker = null;
}
// }
mTouchState = TOUCH_STATE_REST;
break;
case MotionEvent.ACTION_CANCEL:
mTouchState = TOUCH_STATE_REST;
break;
}
Log.d("SCROLLL", "before true "+System.currentTimeMillis());
return true;
}
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
//Log.e(TAG, "onInterceptTouchEvent-slop:" + mTouchSlop);
final int action = ev.getAction();
if ((action == MotionEvent.ACTION_MOVE)
&& (mTouchState != TOUCH_STATE_REST)) {
if(isScroll)
return true;
else
return false;
}
final float x = ev.getX();
final float y = ev.getY();
switch (action) {
case MotionEvent.ACTION_MOVE:
final int xDiff = (int) Math.abs(mLastMotionX - x);
if (xDiff > mTouchSlop) {
mTouchState = TOUCH_STATE_SCROLLING;
}
break;
case MotionEvent.ACTION_DOWN:
mLastMotionX = x;
mLastMotionY = y;
mTouchState = mScroller.isFinished() ? TOUCH_STATE_REST
: TOUCH_STATE_SCROLLING;
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
mTouchState = TOUCH_STATE_REST;
break;
}
if(isScroll)
return mTouchState != TOUCH_STATE_REST;
else
return false;
}
/**
* 设置屏幕切换监听器
* #param listener
*/
public void SetOnViewChangeListener(OnViewChangeListener listener)
{
mOnViewChangeListener = listener;
}
/**
* 屏幕切换监听器
* #author liux
*/
public interface OnViewChangeListener {
public void OnViewChange(int view);
}
#Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
int scrollX = getScrollX();
final int N = getChildCount();
final int width = getWidth();
if (scrollX < 0) {
View lastChild = getChildAt(N - 1);
Log.d("TEST", "draw extra left ,scrollX is : " + scrollX + " width:"+lastChild.getWidth());
// end of first view and draw last view at its left
canvas.save();
canvas.translate(-width, 0);
canvas.clipRect(0, 0, width, getBottom());
lastChild.draw(canvas);
canvas.restore();
} else if (scrollX > (N - 1) * width) {
Log.d("TEST", "draw extra right ,scrollX is : " + scrollX);
View firstChild = getChildAt(0);
canvas.save();
canvas.translate(N * width, 0);
canvas.clipRect(0, 0, width, getBottom());
firstChild.draw(canvas);
canvas.restore();
}
}
}
I have an image(ball) its a BitmapDrawable and it moves in all directions on the screen for that i am taking onDraw(canvas) in AnimatedView.java its working fine.
My Requirement:
I want to click on the image, but unfortunately the whole screen takes the event.
Please help me.
How to get click event only on the image?
So that i can proceed with further development.
Here is the code:
MainActivity
public class MainActivity extends Activity {
Context context;
int count=0;
EditText text;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
AnimatedView animatedView=(AnimatedView) findViewById(R.id.anim_view);
animatedView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
EditText text=(EditText) findViewById(R.id.editText1);
ImageView imageView=(ImageView) findViewById(R.drawable.ball);
v.startAnimation(AnimationUtils.loadAnimation(MainActivity.this, R.anim.animation));
}
});
animatedview.java:
public class AnimatedView extends ImageView{
private Context mContext;
int x = -1;
int y = -1;
private int xVelocity = 10;
private int yVelocity = 5;
private Handler h;
private final int FRAME_RATE = 30;
BitmapDrawable ball;
public AnimatedView(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
h = new Handler();
}
private Runnable r = new Runnable() {
#Override
public void run() {
invalidate();
}
};
#Override
protected void onDraw(Canvas c) {
BitmapDrawable ball = (BitmapDrawable) mContext.getResources().getDrawable(R.drawable.ball);
if (x<0 && y <0) {
x = this.getWidth()/2;
y = this.getHeight()/2;
} else {
x += xVelocity;
y += yVelocity;
if ((x > this.getWidth() - ball.getBitmap().getWidth()) || (x < 0)) {
xVelocity = xVelocity*-1;
}
if ((y > this.getHeight() - ball.getBitmap().getHeight()) || (y < 0)) {
yVelocity = yVelocity*-1;
}
}
c.drawBitmap(ball.getBitmap(), x, y, null);
h.postDelayed(r, FRAME_RATE);
}
main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:background="#000000">
<com.example.example.AnimatedView
android:id="#+id/anim_view"
android:layout_width="fill_parent"
android:layout_height="420dp"
android:onClick="imageClicked" />
</LinearLayout>
**I hope This codes Works for you, what you are looking for.....**
This is the complete code representing both ball movement and touch on ball.
public class MainActivity extends Activity implements OnTouchListener
{
BubbleGraphic bubbleGraphic;
int bm_x = 0, bm_y = 0, bm_offsetx, bm_offsety, bm_w, bm_h, subBM_w, subBM_h;
int f = 0;
private float x, y;
Canvas canvas;
boolean dm_touched = false;
static int random_x;
static int random_y;
boolean touching;
// These variable for position in x_axis direction
int w1;
// These variable for position in y_axis direction
int h1;
// These variable for velocity in x_axis direction
private int w1_V;
// These variable for velocity in y_axis direction
private int h1_V;
Bitmap mainBG;
Bitmap bmpBall;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
bubbleGraphic = new BubbleGraphic(this);
bubbleGraphic.setOnTouchListener(this);
// Your Background Image of Canvas
mainBG = BitmapFactory.decodeResource(getResources(), R.drawable.bg1);
w1 = -1;
h1 = -1;
w1_V = 1;
h1_V = 1;
// This is used to create bitmap object and decode the input stream into bitmap
bmpBall = BitmapFactory.decodeResource(getResources(),
R.drawable.myBall);
bm_w = bmpBall.getWidth();
bm_h = bmpBall.getHeight();
setContentView(bubbleGraphic);
}
protected void onPause() {
super.onPause();
bubbleGraphic.pause();
}
protected void onResume() {
super.onResume();
bubbleGraphic.resume();
}
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE:
x = event.getX();
y = event.getY();
touching = true;
if (dm_touched) {
w1 = (int) x - bm_offsetx;
h1 = (int) y - bm_offsety;
}
break;
case MotionEvent.ACTION_DOWN:
x = event.getX();
y = event.getY();
touching = true;
// checking if Ball is touched
if ((x > w1) && (x < bm_w + w1) && (y > h1) && (y < bm_h + h1)) {
bm_offsetx = (int) x - w1;
bm_offsety = (int) y - h1;
}
dm_touched = true;
break;
case MotionEvent.ACTION_UP:
default:
dm_touched = false;
touching = false;
}
return true;
}
// **************************************************************************************//
// Creating Canvas graphic Class and running thread //
// *************************************************************************************//
class BubbleGraphic extends SurfaceView implements Runnable {
SurfaceHolder holder;
Thread thread = null;
boolean isRunning = false;
boolean holdingBubble = true;
private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
public BubbleGraphic(Context context) {
super(context);
holder = getHolder();
}
public void pause() {
isRunning = false;
while (true) {
try {
thread.join();
} catch (Exception e) {
}
break;
}
thread = null;
}
public void resume() {
isRunning = true;
thread = new Thread(this);
thread.start();
}
public void run() {
while (isRunning) {
if (!holder.getSurface().isValid())
continue;
canvas = holder.lockCanvas();`enter code here`
canvas.drawBitmap(mainBG, 0, 0, null);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(1);
paint.setColor(Color.TRANSPARENT);
if (touching) {
canvas.drawRect(x, y, x + bm_w, y + bm_h, paint);
}
canvas.drawBitmap(bmpBall, w1, h1, null);
// Method Responsible for ball movement and its position
checkBallMovementAndPosition();
holder.unlockCanvasAndPost(canvas);
}
}
private void checkBallMovementAndPosition() {
if (w1 < 0 && h1 < 0) {
w1 = this.getWidth() / 2;
h1 = this.getHeight() / 2;
} else {
w1 += w1_V;
h1 += h1_V;
if ((w1 > this.getWidth() - bm_w) || (w1 < 0)) {
w1_V = w1_V * -1;
w1 += w1_V;
}
if ((h1 > this.getHeight() - bm_h) || (h1 < 0)) {
h1_V = h1_V * -1;
h1 += h1_V;
}
}
}
}
}
Note: Replace the images with Your images.
I am using a custom layout to display a varied number of buttons and intercept their clicks. Here is the source code for the custom layout:
import android.content.Context;
import android.content.res.TypedArray;
import android.database.DataSetObserver;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Adapter;
import android.widget.AdapterView;
public class FlowLayout extends AdapterView<Adapter> {
public static final int HORIZONTAL = 0;
public static final int VERTICAL = 1;
private static final int INVALID_INDEX = -1;
private static final int TOUCH_STATE_RESTING = 0;
private static final int TOUCH_STATE_CLICK = 1;
private int mTouchState = TOUCH_STATE_RESTING;
private Rect mRect;
private Runnable mLongPressRunnable;
private int mTouchStartX;
private int mTouchStartY;
private int horizontalSpacing = 0;
private int verticalSpacing = 0;
private int orientation = 0;
private boolean debugDraw = false;
private Adapter mAdapter;
private final AdapterObserver mObserver = new AdapterObserver();
public FlowLayout(Context context) {
super(context);
this.readStyleParameters(context, null);
}
public FlowLayout(Context context, AttributeSet attributeSet) {
super(context, attributeSet);
this.readStyleParameters(context, attributeSet);
}
public FlowLayout(Context context, AttributeSet attributeSet, int defStyle) {
super(context, attributeSet, defStyle);
this.readStyleParameters(context, attributeSet);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int sizeWidth = MeasureSpec.getSize(widthMeasureSpec) - this.getPaddingRight() - this.getPaddingLeft();
int sizeHeight = MeasureSpec.getSize(heightMeasureSpec) - this.getPaddingRight() - this.getPaddingLeft();
int modeWidth = MeasureSpec.getMode(widthMeasureSpec);
int modeHeight = MeasureSpec.getMode(heightMeasureSpec);
int size;
int mode;
if (orientation == HORIZONTAL) {
size = sizeWidth;
mode = modeWidth;
} else {
size = sizeHeight;
mode = modeHeight;
}
int lineThicknessWithSpacing = 0;
int lineThickness = 0;
int lineLengthWithSpacing = 0;
int lineLength;
int prevLinePosition = 0;
int controlMaxLength = 0;
int controlMaxThickness = 0;
final int count = getChildCount();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() == GONE) {
continue;
}
child.measure(
MeasureSpec.makeMeasureSpec(sizeWidth, modeWidth == MeasureSpec.EXACTLY ? MeasureSpec.AT_MOST : modeWidth),
MeasureSpec.makeMeasureSpec(sizeHeight, modeHeight == MeasureSpec.EXACTLY ? MeasureSpec.AT_MOST : modeHeight)
);
LayoutParams lp = (LayoutParams) child.getLayoutParams();
int hSpacing = this.getHorizontalSpacing(lp);
int vSpacing = this.getVerticalSpacing(lp);
int childWidth = child.getMeasuredWidth();
int childHeight = child.getMeasuredHeight();
int childLength;
int childThickness;
int spacingLength;
int spacingThickness;
if (orientation == HORIZONTAL) {
childLength = childWidth;
childThickness = childHeight;
spacingLength = hSpacing;
spacingThickness = vSpacing;
} else {
childLength = childHeight;
childThickness = childWidth;
spacingLength = vSpacing;
spacingThickness = hSpacing;
}
lineLength = lineLengthWithSpacing + childLength;
lineLengthWithSpacing = lineLength + spacingLength;
boolean newLine = lp.newLine || (mode != MeasureSpec.UNSPECIFIED && lineLength > size);
if (newLine) {
prevLinePosition = prevLinePosition + lineThicknessWithSpacing;
lineThickness = childThickness;
lineLength = childLength;
lineThicknessWithSpacing = childThickness + spacingThickness;
lineLengthWithSpacing = lineLength + spacingLength;
}
lineThicknessWithSpacing = Math.max(lineThicknessWithSpacing, childThickness + spacingThickness);
lineThickness = Math.max(lineThickness, childThickness);
int posX;
int posY;
if (orientation == HORIZONTAL) {
posX = getPaddingLeft() + lineLength - childLength;
posY = getPaddingTop() + prevLinePosition;
} else {
posX = getPaddingLeft() + prevLinePosition;
posY = getPaddingTop() + lineLength - childHeight;
}
lp.setPosition(posX, posY);
controlMaxLength = Math.max(controlMaxLength, lineLength);
controlMaxThickness = prevLinePosition + lineThickness;
}
if (orientation == HORIZONTAL) {
this.setMeasuredDimension(resolveSize(controlMaxLength, widthMeasureSpec), resolveSize(controlMaxThickness, heightMeasureSpec));
} else {
this.setMeasuredDimension(resolveSize(controlMaxThickness, widthMeasureSpec), resolveSize(controlMaxLength, heightMeasureSpec));
}
}
private int getVerticalSpacing(LayoutParams lp) {
int vSpacing;
if (lp.verticalSpacingSpecified()) {
vSpacing = lp.verticalSpacing;
} else {
vSpacing = this.verticalSpacing;
}
return vSpacing;
}
private int getHorizontalSpacing(LayoutParams lp) {
int hSpacing;
if (lp.horizontalSpacingSpecified()) {
hSpacing = lp.horizontalSpacing;
} else {
hSpacing = this.horizontalSpacing;
}
return hSpacing;
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
final int count = getChildCount();
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
LayoutParams lp = (LayoutParams) child.getLayoutParams();
child.layout(lp.x, lp.y, lp.x + child.getMeasuredWidth(), lp.y + child.getMeasuredHeight());
}
}
#Override
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
boolean more = super.drawChild(canvas, child, drawingTime);
this.drawDebugInfo(canvas, child);
return more;
}
#Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
return p instanceof LayoutParams;
}
#Override
protected LayoutParams generateDefaultLayoutParams() {
return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
}
#Override
public LayoutParams generateLayoutParams(AttributeSet attributeSet) {
return new LayoutParams(getContext(), attributeSet);
}
#Override
protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
return new LayoutParams(p);
}
private void readStyleParameters(Context context, AttributeSet attributeSet) {
TypedArray a = context.obtainStyledAttributes(attributeSet, R.styleable.FlowLayout);
try {
horizontalSpacing = a.getDimensionPixelSize(R.styleable.FlowLayout_horizontalSpacing, 0);
verticalSpacing = a.getDimensionPixelSize(R.styleable.FlowLayout_verticalSpacing, 0);
orientation = a.getInteger(R.styleable.FlowLayout_orientation, HORIZONTAL);
debugDraw = a.getBoolean(R.styleable.FlowLayout_debugDraw, false);
} finally {
a.recycle();
}
}
private void drawDebugInfo(Canvas canvas, View child) {
if (!debugDraw) {
return;
}
Paint childPaint = this.createPaint(0xffffff00);
Paint layoutPaint = this.createPaint(0xff00ff00);
Paint newLinePaint = this.createPaint(0xffff0000);
LayoutParams lp = (LayoutParams) child.getLayoutParams();
if (lp.horizontalSpacing > 0) {
float x = child.getRight();
float y = child.getTop() + child.getHeight() / 2.0f;
canvas.drawLine(x, y, x + lp.horizontalSpacing, y, childPaint);
canvas.drawLine(x + lp.horizontalSpacing - 4.0f, y - 4.0f, x + lp.horizontalSpacing, y, childPaint);
canvas.drawLine(x + lp.horizontalSpacing - 4.0f, y + 4.0f, x + lp.horizontalSpacing, y, childPaint);
} else if (this.horizontalSpacing > 0) {
float x = child.getRight();
float y = child.getTop() + child.getHeight() / 2.0f;
canvas.drawLine(x, y, x + this.horizontalSpacing, y, layoutPaint);
canvas.drawLine(x + this.horizontalSpacing - 4.0f, y - 4.0f, x + this.horizontalSpacing, y, layoutPaint);
canvas.drawLine(x + this.horizontalSpacing - 4.0f, y + 4.0f, x + this.horizontalSpacing, y, layoutPaint);
}
if (lp.verticalSpacing > 0) {
float x = child.getLeft() + child.getWidth() / 2.0f;
float y = child.getBottom();
canvas.drawLine(x, y, x, y + lp.verticalSpacing, childPaint);
canvas.drawLine(x - 4.0f, y + lp.verticalSpacing - 4.0f, x, y + lp.verticalSpacing, childPaint);
canvas.drawLine(x + 4.0f, y + lp.verticalSpacing - 4.0f, x, y + lp.verticalSpacing, childPaint);
} else if (this.verticalSpacing > 0) {
float x = child.getLeft() + child.getWidth() / 2.0f;
float y = child.getBottom();
canvas.drawLine(x, y, x, y + this.verticalSpacing, layoutPaint);
canvas.drawLine(x - 4.0f, y + this.verticalSpacing - 4.0f, x, y + this.verticalSpacing, layoutPaint);
canvas.drawLine(x + 4.0f, y + this.verticalSpacing - 4.0f, x, y + this.verticalSpacing, layoutPaint);
}
if (lp.newLine) {
if (orientation == HORIZONTAL) {
float x = child.getLeft();
float y = child.getTop() + child.getHeight() / 2.0f;
canvas.drawLine(x, y - 6.0f, x, y + 6.0f, newLinePaint);
} else {
float x = child.getLeft() + child.getWidth() / 2.0f;
float y = child.getTop();
canvas.drawLine(x - 6.0f, y, x + 6.0f, y, newLinePaint);
}
}
}
private Paint createPaint(int color) {
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(color);
paint.setStrokeWidth(2.0f);
return paint;
}
public static class LayoutParams extends ViewGroup.LayoutParams {
private static int NO_SPACING = -1;
private int x;
private int y;
private int horizontalSpacing = NO_SPACING;
private int verticalSpacing = NO_SPACING;
private boolean newLine = false;
public LayoutParams(Context context, AttributeSet attributeSet) {
super(context, attributeSet);
this.readStyleParameters(context, attributeSet);
}
public LayoutParams(int width, int height) {
super(width, height);
}
public LayoutParams(ViewGroup.LayoutParams layoutParams) {
super(layoutParams);
}
public boolean horizontalSpacingSpecified() {
return horizontalSpacing != NO_SPACING;
}
public boolean verticalSpacingSpecified() {
return verticalSpacing != NO_SPACING;
}
public void setHorizontalSpacing(int hs) {
horizontalSpacing = hs;
}
public void setVerticalSpacing(int vs) {
verticalSpacing = vs;
}
public void setPosition(int x, int y) {
this.x = x;
this.y = y;
}
private void readStyleParameters(Context context, AttributeSet attributeSet) {
TypedArray a = context.obtainStyledAttributes(attributeSet, R.styleable.FlowLayout_LayoutParams);
try {
horizontalSpacing = a.getDimensionPixelSize(R.styleable.FlowLayout_LayoutParams_layout_horizontalSpacing, NO_SPACING);
verticalSpacing = a.getDimensionPixelSize(R.styleable.FlowLayout_LayoutParams_layout_verticalSpacing, NO_SPACING);
newLine = a.getBoolean(R.styleable.FlowLayout_LayoutParams_layout_newLine, false);
} finally {
a.recycle();
}
}
}
#Override
public Adapter getAdapter() {
return mAdapter;
}
#Override
public View getSelectedView() {
throw new UnsupportedOperationException("Not supported");
}
#Override
public void setAdapter(Adapter adapter) {
if (mAdapter != null) {
mAdapter.unregisterDataSetObserver(mObserver);
}
mAdapter = adapter;
mAdapter.registerDataSetObserver(mObserver);
refresh();
}
public void refresh() {
removeAllViewsInLayout();
for (int i = 0; i < mAdapter.getCount(); i++) {
final View view = mAdapter.getView(i, null, this);
ViewGroup.LayoutParams params = view.getLayoutParams();
if (params == null) {
params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
}
addViewInLayout(view, i, params, true);
}
postInvalidate();
requestLayout();
}
public class AdapterObserver extends DataSetObserver {
#Override
public void onChanged() {
refresh();
}
#Override
public void onInvalidated() {
}
}
#Override
public void setSelection(int position) {
throw new UnsupportedOperationException("Not supported");
}
// Touch detection
#Override
public boolean onTouchEvent(MotionEvent event) {
if (getChildCount() == 0) {
return false;
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
startTouch(event);
break;
case MotionEvent.ACTION_UP:
if (mTouchState == TOUCH_STATE_CLICK) {
clickChildAt((int)event.getX(), (int)event.getY());
}
endTouch();
break;
default:
endTouch();
break;
}
return true;
}
/**
* Sets and initializes all things that need to when we start a touch
* gesture.
*
* #param event The down event
*/
private void startTouch(final MotionEvent event) {
mTouchStartX = (int)event.getX();
mTouchStartY = (int)event.getY();
startLongPressCheck();
mTouchState = TOUCH_STATE_CLICK;
}
private void startLongPressCheck() {
if (mLongPressRunnable == null) {
mLongPressRunnable = new Runnable() {
public void run() {
if (mTouchState == TOUCH_STATE_CLICK) {
final int index = getContainingChildIndex(mTouchStartX, mTouchStartY);
if (index != INVALID_INDEX) { longClickChild(index); mTouchState = TOUCH_STATE_RESTING; }
}
}
};
}
postDelayed(mLongPressRunnable, 300); //ViewConfiguration.getLongPressTimeout()
}
private void longClickChild(final int index) {
final View itemView = getChildAt(index);
final int position = index;
final long id = mAdapter.getItemId(position);
final OnItemLongClickListener listener = getOnItemLongClickListener();
if (listener != null) { listener.onItemLongClick(this, itemView, position, id); }
}
private int getContainingChildIndex(final int x, final int y) {
if (mRect == null) { mRect = new Rect(); }
for (int index = 0; index < getChildCount(); index++) {
getChildAt(index).getHitRect(mRect);
if (mRect.contains(x, y)) { return index; }
}
return INVALID_INDEX;
}
private void endTouch() {
removeCallbacks(mLongPressRunnable);
mTouchState = TOUCH_STATE_RESTING;
}
private void clickChildAt(final int x, final int y) {
final int index = getContainingChildIndex(x, y);
if (index != INVALID_INDEX) {
final View itemView = getChildAt(index);
final int position = index;
final long id = mAdapter.getItemId(position);
performItemClick(itemView, position, id);
}
}
}
This code works on my test device which is a Google Nexus S with Android 4.1.2, meaning that the buttons are clickable. Yet I got reports that the buttons are unresponsive on other devices such as Android Casio C771 with Android version 2.3.3 and Verizon LG VS840 with Android 4.0.4.
Can you please tell me what could cause this discrepancy and how can I fix it?
Thank you
Things to look out for:
It is essential that the Views created for the adapter are enabled for click and touch action. After they are created:
View.setEnabled(true);
When you create the AdapterView and the BaseApapter, remember to both setOnItemClickListener and setOnItemLongClickListener if you want to handle long clicks, which it seems you do.
In the endTouch routine, you need to set mLongPressRunnable to null, so it will be created for the next touch (or lose the null check in startLongPressCheck and handle concurency issues another way).
The runnable created in startLongPressCheck needs to call endTouch when complete to reset everything to the right, nothing pending, state after the long press time period.
In the onTouchEvent switch statement, it is important to not call your endTouch routine, because this means that movement events may stop the click.
There is a suggestion (see Android onClick method doesn't work on a custom view) that if you don't call super.onTouchEvent(event) in your onTouchEvent it can cause problems. This would look something like:
#Override
public boolean onTouchEvent(MotionEvent event) {
if (getChildCount() == 0) {
return super.onTouchEvent(event);
}
boolean handled = false;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
startTouch(event);
handled = true;
break;
case MotionEvent.ACTION_UP:
if (mTouchState == TOUCH_STATE_CLICK) {
clickChildAt((int)event.getX(), (int)event.getY());
handled = true;
}
endTouch();
break;
default:
// Do not do anything dramatic here because the system
// may send different (eg. motion) information here which
// may lead to you losing valid clicks if you simply cancel
// the click in process.
break;
}
if (handled == false) handled = super.onTouchEvent(event);
return handled;
}