I have created on basic View which has Red color in background.I am using that view in my activity.I have setOnTouch listner to same view.
on touch events i am moving that view.
Before touch event it looks like this
After MotionEvent.ACTION_MOVE it look like this
After motion event i want view to be redrawn.I don't want white portion to be shown.
it should redraw it like google map does on moving map in any direction.
following code is of my basic custom view
public class CustomVIew extends View {
public CustomVIew(Context context) {
super(context);
}
public CustomVIew(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomVIew(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.RED);
}
}
This is my OnTouch method implementation
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
_xDelta = v.getX() - event.getRawX();
_yDelta = v.getY() - event.getRawY();
Log.d(TAG, "onTouch: X:" + _xDelta + " Y:" + _yDelta);
break;
case MotionEvent.ACTION_MOVE:
v.animate()
.x(event.getRawX() + _xDelta)
.y(event.getRawY() + _yDelta)
.setDuration(10)
.start();
break;
default:
return false;
}
return true;
}
Thanx in advance
you may need to invalidate the view .
this helps:
When it's necessary to execute invalidate() on a View?
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
_xDelta = v.getX() - event.getRawX();
_yDelta = v.getY() - event.getRawY();
Log.d(TAG, "onTouch: X:" + _xDelta + " Y:" + _yDelta);
break;
case MotionEvent.ACTION_MOVE:
//do something like change the picture
v.invalidate();
break;
default:
return false;
}
return true;
}
Related
I'm new to Java and android development and I want to create this basic app, which consists of multiple CustomViews, where I could move different shapes drawn on canvas.
My idea is to have one class that handles MotionEvents and passes them to individual CustomViews.
My CustomView class:
public class CustomView extends View implements MoveDetector.OnMoveListener {
private MoveDetector mMoveDetector;
public CustomView(Context context) {
super(context);
}
public CustomView(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs);
}
public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(attrs);
}
#RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public CustomView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(attrs);
}
private void init(#Nullable AttributeSet set){
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.parseColor("#626696"));
}
#Override
public boolean onTouchEvent(MotionEvent event){
switch (event.getAction()){
case MotionEvent.ACTION_MOVE:
mMoveDetector = new MoveDetector(this);
mMoveDetector.onTouchEvent(event);
float cx = mMoveDetector.getCx();
float cy = mMoveDetector.getCy();
Toast.makeText(getContext(),"why u packin heat bro?"+Float.toString(cx)+" "+Float.toString(cy),Toast.LENGTH_SHORT).show();
Log.d("Moveee", "Move: " + Float.toString(cx)+" " + Float.toString(cy));
}
return super.onTouchEvent(event);
}
#Override
public void OnMove(MoveDetector moveDetector) {
}
}
My MoveDetector Class:
public class MoveDetector {
private float cx,cy, mCislo1,mCislo2;
public float getCx() {
return mCislo1;
}
public float getCy() {
return mCislo2;
}
private OnMoveListener mListener;
public MoveDetector(OnMoveListener listener){
mListener = listener;
}
public boolean onTouchEvent(MotionEvent event) {
switch (event.getActionMasked()) {
case MotionEvent.ACTION_MOVE:
float x = event.getX();
float y = event.getY();
mCislo1 = cXX(x);
mCislo2 = cYY(y);
if (mListener != null) {
mListener.OnMove(this);
}
break;
}
return true;}
public static interface OnMoveListener {
public void OnMove(MoveDetector moveDetector);
}
public float cXX(float x){
cx = x;
return cx;
}
public float cYY(float y){
cy = y;
return cy;
}
}
It doesn't seem to be working and I can't point why.
I searched a solution for ages and couldn't find any.
So I'd be really thankful if someone could help me out.
P.S.: Sorry, if my code is messy: I'm still learning.
I worked on android swipe gestures, i applied swipe gesture on Item inside recycler view and recycler view is inside fragment and then fragment is inside activity and activity has Scroll View, The problem was that parent activity was intercepting swipe gesture of item inside child recycler view, After so much hard i found solution and this code is working fine, Is there any other better solution to this problem? if yes then post it in answer otherwise vote my code so that i can use it in my app without any fear of failure. Here is my code
listingView.setOnTouchListener(new OnSwipeTouchListener(myContext)
{
public void onClick() {
ViewListing();
}
// #Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getActionMasked())
{
case MotionEvent.ACTION_DOWN:
x1 = event.getX();
break;
case MotionEvent.ACTION_UP:
LockableScrollView.setScrollingEnabled(true);
break;
case MotionEvent.ACTION_MOVE:
x2 = event.getX();
float deltaX = x2 - x1;
if (Math.abs(deltaX) > 20)
{
LockableScrollView.setScrollingEnabled(false);
// Left to Right swipe action
if (x2 > x1)
{
hideSidePanel();
LockableScrollView.setScrollingEnabled(true);
}
else
//MH: Right to Left Swipe
{
showSidePanel();
LockableScrollView.setScrollingEnabled(true);
}
}
break;
}
return super.onTouch(v, event);
}
});
Lockable Scroll View code
public class LockableScrollView extends ScrollView {
#Override
public void setClipChildren(boolean clipChildren) {
clipChildren=false;
super.setClipChildren(clipChildren);
}
#Override
public void setFocusable(int focusable) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
focusable=View.NOT_FOCUSABLE;
}
super.setFocusable(focusable);
}
// true if we can scroll (not locked)
// false if we cannot scroll (locked)
private static boolean mScrollable = true;
public LockableScrollView(Context context) {
super(context);
}
public LockableScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public LockableScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public LockableScrollView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
public static void setScrollingEnabled(boolean enabled) {
mScrollable = enabled;
}
public static boolean isScrollable() {
return mScrollable;
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
//MH: if we can scroll pass the event to the superclass
if (mScrollable) return super.onTouchEvent(ev);
//MH: only continue to handle the touch event if scrolling enabled
return mScrollable; // mScrollable is always false at this point
default:
return super.onTouchEvent(ev);
}
}
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
//MH: Don't do anything with intercepted touch events if
//MH: we are not scrollable
if (!mScrollable) return false;
else return super.onInterceptTouchEvent(ev);
}
}
I've tried extending LinearLayout instead of simple View for adding buttons and working children of View but I am still not getting any output. Conceptually I'm wrong somewhere.
public class TouchEventView extends LinearLayout {
private Paint paint = new Paint();
private Path path = new Path();
private ViewGroup viewGroup;
public TouchEventView(Context ctx) {
super(ctx);
//Button Code Starts Here
LinearLayout touch = new TouchEventView(ctx);
Button bt = new Button(ctx);
bt.setText("A Button");
bt.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,LayoutParams.WRAP_CONTENT));
touch.addView(bt); //Button Not Working
paint.setAntiAlias(true);
paint.setColor(Color.BLACK);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(5f);
this.setBackgroundColor(Color.WHITE);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawPath(path,paint);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
float xPos = event.getX();
float yPos = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
path.moveTo(xPos,yPos);
return true;
case MotionEvent.ACTION_MOVE:
path.lineTo(xPos,yPos);
break;
case MotionEvent.ACTION_UP:
break;
default:
return false;
}
invalidate();
return true;
}
}
The issue is that you are creating a new TouchEventView and adding the Button to that View. Instead you should add the Button directly to the current View.
You should also implement the other constructors from LinearLayout if you want to be able to get any attributes from XML.
public class TouchEventView extends LinearLayout {
public TouchEventView(Context context) {
this(context, null);
}
public TouchEventView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public TouchEventView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
Button button = new Button(getContext());
button.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
addView(button);
}
}
I have a layout like that:
<NestedScrollView>
<RecyclerView> // vertical recycler view
<RecyclerView/> // horizontal recycler view
<RecyclerView/>
<RecyclerView/>
...
<RecyclerView>
</NestedScrollView>
The result looks like Google play store:
And I disabled NestedScrolling in horizontal Recycler view:
horizontalRecyclerView.setHasFixedSize(true);
horizontalRecyclerView.setNestedScrollingEnabled(false);
My problem:
The vertical recyclerview does not scroll fling, whenever ACTION_UP happen, the vertical recyclerview also stop scrolling.
How can I nest vertical recyclerview inside nestedscrollview, and horizontal recyclerview inside vertical recyclerview like Playstore and keep the scroll smooth.
Solved:
Using custom nested scroll view of #vrund purohit (code below), and disabled nestedscroll both vertical and horizontal recyclerview:
verticalRecyclerView.setNestedScrollingEnabled(false);
... add each horizontal recyclerviews:
horizontalRecyclerView.setNestedScrollingEnabled(false);
Use below code for smooth scroll:
ViewCompat.setNestedScrollingEnabled(recyclerView, false);
Add this in your RecyclerView xml:
android:nestedScrollingEnabled="false"
I had this same problem and I solved this issue by customizing NeatedScrollView.
Here is the class for that.
MyNestedScrollView
public class MyNestedScrollView extends NestedScrollView {
#SuppressWarnings("unused")
private int slop;
#SuppressWarnings("unused")
private float mInitialMotionX;
#SuppressWarnings("unused")
private float mInitialMotionY;
public MyNestedScrollView(Context context) {
super(context);
init(context);
}
private void init(Context context) {
ViewConfiguration config = ViewConfiguration.get(context);
slop = config.getScaledEdgeSlop();
}
public MyNestedScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public MyNestedScrollView(Context context, AttributeSet attrs,
int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private float xDistance, yDistance, lastX, lastY;
#SuppressWarnings("unused")
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
final float x = ev.getX();
final float y = ev.getY();
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
xDistance = yDistance = 0f;
lastX = ev.getX();
lastY = ev.getY();
// This is very important line that fixes
computeScroll();
break;
case MotionEvent.ACTION_MOVE:
final float curX = ev.getX();
final float curY = ev.getY();
xDistance += Math.abs(curX - lastX);
yDistance += Math.abs(curY - lastY);
lastX = curX;
lastY = curY;
if (xDistance > yDistance) {
return false;
}
}
return super.onInterceptTouchEvent(ev);
}
public interface OnScrollChangedListener {
void onScrollChanged(NestedScrollView who, int l, int t, int oldl,
int oldt);
}
private OnScrollChangedListener mOnScrollChangedListener;
public void setOnScrollChangedListener(OnScrollChangedListener listener) {
mOnScrollChangedListener = listener;
}
#Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
if (mOnScrollChangedListener != null) {
mOnScrollChangedListener.onScrollChanged(this, l, t, oldl, oldt);
}
}
}
Happy coding.
[RESOLVED] I have same issue with Horizontal recycleview. Change Gradle repo for recycleview
compile 'com.android.support:recyclerview-v7:23.2.1'
Write this: linearLayoutManager.setAutoMeasureEnabled(true);
Fixed bugs related to various measure-spec methods in update
Check http://developer.android.com/intl/es/tools/support-library/features.html#v7-recyclerview
I have found issue with 23.2.1 library: When item is match_parent recycle view fill full item to view, please always go with min height or "wrap_content".
Thanks
I've solved the issue by using below code:
myRecyclerView.setLayoutManager(new LinearLayoutManager(getContext(), LinearLayoutManager.VERTICAL, false){
#Override
public boolean canScrollHorizontally() {
return true;
}
#Override
public boolean canScrollVertically() {
return true;
}
});
I am trying to implement a vertical seekbar in a horizontal scroll-view,
but the seekbar is not working smoothly in it.
This is my class for vertical sekkbar .....Source from stackoverflow only .
public class VerticalSeekBar extends SeekBar {
public VerticalSeekBar(Context context) {
super(context);
}
public VerticalSeekBar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public VerticalSeekBar(Context context, AttributeSet attrs) {
super(context, attrs);
}
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(h, w, oldh, oldw);
}
#Override
protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(heightMeasureSpec, widthMeasureSpec);
setMeasuredDimension(getMeasuredHeight(), getMeasuredWidth());
}
protected void onDraw(Canvas c) {
c.rotate(-90);
c.translate(-getHeight(), 0);
super.onDraw(c);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (!isEnabled()) {
return false;
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
case MotionEvent.ACTION_UP:
setProgress(getMax() - (int) (getMax() * event.getY() / getHeight()));
onSizeChanged(getWidth(), getHeight(), 0, 0);
break;
case MotionEvent.ACTION_CANCEL:
break;
}
return true;
}
}
This is the way i am implementing seekbar .
seekbar3.setMax(5000);
seekbar3.setProgress(mEqualizer.getBandLevel(band));
seekbar3.setFocusable(false);
seekbar3.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
horizontalscroll.setOnTouchListener(null);
horizontalscroll.setEnabled(false);
mHandler.removeCallbacks(mUpdateTimeTask);
seekbar3.setProgress(mEqualizer.getBandLevel(band));
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
//horizontalscroll.setEnabled(false);
/*horizontalscroll.setOnTouchListener(new View.OnTouchListener(){
#Override
public boolean onTouch(View v, MotionEvent event) {
return true;
}
});*/
ObjectAnimator animator=ObjectAnimator.ofInt(horizontalscroll, "scrollX",8,8 );
animator.setDuration(2000);
animator.start();
// seekbar3.setProgress(mEqualizer.getBandLevel(band));
mHandler.removeCallbacks(mUpdateTimeTask);
// Enable Scrolling by removing the OnTouchListner
// horizontalscroll.setOnTouchListener(null);
}
#Override
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser) {
mEqualizer.setBandLevel(band, (short)(progress+min));
seekbar3top.setText((progress+min)+"mB");
seekbar3.setProgress(progress);
// seekbar3.onDraw(c)
/* horizontalscroll.setOnTouchListener(new View.OnTouchListener(){
#Override
public boolean onTouch(View v, MotionEvent event) {
return true;
}});*/
}
});
}
Can anyone suggest me how to do in a simple way.
My verticalseekbar doesnt move smoothly like it should move.
Thanx in advance :)
Finally i solved this problem
i changed my vertical seekbar class to this
import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.SeekBar;
public class VerticalSeekBar extends SeekBar {
private boolean mIsMovingThumb = false;
static private float THUMB_SLOP = 25;
public VerticalSeekBar(Context context) {
super(context);
}
public VerticalSeekBar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public VerticalSeekBar(Context context, AttributeSet attrs) {
super(context, attrs);
}
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(h, w, oldh, oldw);
}
#Override
protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(heightMeasureSpec, widthMeasureSpec);
setMeasuredDimension(getMeasuredHeight(), getMeasuredWidth());
}
protected void onDraw(Canvas c) {
c.rotate(-90);
c.translate(-getHeight(), 0);
super.onDraw(c);
}
private boolean isWithinThumb(MotionEvent event) {
final float progress = getProgress();
final float density = this.getResources().getDisplayMetrics().density;
final float height = getHeight();
final float y = event.getY();
final float max = getMax();
if (progress >= max - (int)(max * (y + THUMB_SLOP * density) / height)
&& progress <= max - (int)(max * (y - THUMB_SLOP * density) / height))
return true;
else
return false;
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (!isEnabled()) {
return false;
}
boolean handled=false;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (isWithinThumb(event)) {
getParent().requestDisallowInterceptTouchEvent(true);
mIsMovingThumb = true;
handled = true;
}
break;
case MotionEvent.ACTION_MOVE:
case MotionEvent.ACTION_UP:
setProgress(getMax() - (int) (getMax() * event.getY() / getHeight()));
onSizeChanged(getWidth(), getHeight(), 0, 0);
if (mIsMovingThumb) {
setProgress(getMax() - (int) (getMax() * event.getY() / getHeight()));
onSizeChanged(getWidth(), getHeight(), 0, 0);
handled = true;
}
break;
case MotionEvent.ACTION_CANCEL:
getParent().requestDisallowInterceptTouchEvent(false);
mIsMovingThumb = false;
handled = true;
break;
}
return true;
}
}