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.
Related
would try to move multiple points along an L shaped path to simulate water flow from a point to another. I created a view that allow to create a single point and created an animation. Now I'm stuck as I cannot understand how to retrieve values from the animator add points to it and update animation. Can someone try to move me on right path?
public class FlowAnimation extends View implements ValueAnimator.AnimatorUpdateListener {
int radius = 20;
int color = Color.WHITE;
Paint paint = null;
private ValueAnimator mAnimator;
PathMeasure pm;
float point[] = {0f, 0f};
Path path;
public FlowAnimation(Context context) {
super(context);
paint = new Paint();
path = new Path();
path.lineTo(100,150);
path.lineTo(300,500);
path.close();
}
public FlowAnimation(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
paint = new Paint();
}
public FlowAnimation(Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public FlowAnimation(Context context, #Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
super.onDraw(canvas);
int x = getWidth();
int y = getHeight();
paint.setColor(this.color);
canvas.drawCircle(x / 2, y / 2, this.radius, paint);
}
public void startAnim() {
// sets the range of our value
mAnimator = ValueAnimator.ofInt(0, 180);
// sets the duration of our animation
mAnimator.setDuration(1000);
// registers our AnimatorUpdateListener
mAnimator.addUpdateListener(this);
mAnimator.start();
}
#Override
public void onAnimationUpdate(#NonNull ValueAnimator valueAnimator) {
//gets the current value of our animation
int value = (int) valueAnimator.getAnimatedValue();
}
}
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 want to achieve something like this.
but instead of that, I'm getting something like this.
I'm using invalidate to redraw my custom view. But it is creating another view everytime the sides of polygon is changed. Where am I going wrong?
Here is my code.
PolygonView.Java
public class PolygonView extends View {
public float polygonRadius;
public int polygonSides;
public int polygonColor;
private Paint paint;
private Path path;
public PolygonView(Context context) {
super(context);
init(null);
}
public PolygonView(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
init(attrs);
}
public PolygonView(Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(attrs);
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public PolygonView(Context context, #Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(attrs);
}
private void init(AttributeSet attrs)
{
polygonColor = ContextCompat.getColor(getContext(),R.color.polygonColor);
polygonSides = 5;
if (attrs!=null){
TypedArray typedArray = getContext().getTheme().obtainStyledAttributes(attrs,R.styleable.PolygonView,0,0);
polygonColor = typedArray.getColor(R.styleable.PolygonView_polygon_color,polygonColor);
polygonRadius = typedArray.getDimension(R.styleable.PolygonView_polygon_radius,polygonRadius);
polygonSides = typedArray.getInteger(R.styleable.PolygonView_polygon_sides,polygonSides);
}
paint = new Paint();
paint.setColor(polygonColor);
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(5);
path = new Path();
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
double angle = 2.0*Math.PI/polygonSides;
int cx = getWidth()/2;
int cy = getHeight()/2;
path.moveTo(
(float) (cx +polygonRadius*Math.cos(0.0)),
(float) (cy +polygonRadius*Math.sin(0.0))
);
for(int i=1;i<=polygonSides;i++){
path.lineTo(
(float) (cx + polygonRadius*Math.cos(angle*i)),
(float) (cy + polygonRadius*Math.sin(angle*i))
);
}
path.close();
canvas.drawPath(path,paint);
}
}
Fragment2.Java
public class Fragment2 extends Fragment {
public Fragment2() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View v = inflater.inflate(R.layout.fragment_polygon, container, false);
final PolygonView polygonView = v.findViewById(R.id.polygonView);
SeekBar seekBarRadius = v.findViewById(R.id.seekBarRadius);
SeekBar seekBarSides = v.findViewById(R.id.seekBarSides);
seekBarSides.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
int p = progress/10;
if(p<1){
polygonView.polygonSides = 1;
}else if (p>10){
polygonView.polygonSides = 10;
}
else {
polygonView.polygonSides = p;
}
polygonView.invalidate();
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
return v;
}
}
You are always mutating your path but never reset it. Inside onDraw() method reset the path and only then apply new operations to it:
public void onDraw() {
path.reset();
...
}
I have a custom ImageButton and I want to change it's image on click. So far I havent been able to get the onClick method inside the button to perform it's action.
public class FlashButtonView extends ImageButton{
private Drawable mFlashOffSrc, mFlashOnSrc, mFlashAutoSrc;
private Drawable mCurrentFlashMode = mFlashAutoSrc;
public FlashButtonView(Context context) {
super(context);
}
public FlashButtonView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context.getTheme().obtainStyledAttributes(attrs, R.styleable.FlashButtonView, 0, 0));
}
public FlashButtonView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context.getTheme().obtainStyledAttributes(attrs, R.styleable.FlashButtonView, 0, defStyleAttr));
}
private void init(TypedArray typedArray) {
try {
mFlashAutoSrc = typedArray.getDrawable(R.styleable.FlashButtonView_autoSrcImage);
mFlashOnSrc = typedArray.getDrawable(R.styleable.FlashButtonView_onSrcImage);
mFlashOffSrc = typedArray.getDrawable(R.styleable.FlashButtonView_offSrcImage);
} finally {
typedArray.recycle();
}
setImageDrawable(mFlashAutoSrc);
mCurrentFlashMode = mFlashAutoSrc;
setOnClickListener( new OnClickListener() {
#Override
public void onClick(View v) {
if(mCurrentFlashMode == mFlashAutoSrc) {
setImageDrawable(mFlashOnSrc);
mCurrentFlashMode = mFlashOnSrc;
}
else if(mCurrentFlashMode == mFlashOnSrc){
setImageDrawable(mFlashOffSrc);
mCurrentFlashMode = mFlashOffSrc;
}
else{
setImageDrawable(mFlashAutoSrc);
mCurrentFlashMode = mFlashAutoSrc;
}
}
});
}
}
And this is how my XML looks like:
<!--.___ Flash on/off switcher __.-->
<blablabla.FlashButtonView
custom:autoSrcImage="#drawable/button_autoflash"
custom:offSrcImage="#drawable/button_noflash"
custom:onSrcImage="#drawable/button_flash"
android:id="#+id/flash"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_marginLeft="#dimen/distance_20dp"
android:layout_marginStart="#dimen/distance_20dp"
android:layout_marginBottom="#dimen/distance_20dp"
android:background="#android:color/transparent"
android:clickable="true"/>
What am I missing or doing wrong?
Implement onTouchEvent() method. Something like this:
float touched_x, touched_y;
boolean touched = false;
#Override public boolean onTouchEvent(MotionEvent event) {
touchCounter++;
touched_x = event.getX();
touched_y = event.getY();
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
touched = true;
break;
case MotionEvent.ACTION_MOVE:
touched = true;
break;
case MotionEvent.ACTION_UP:
touched = false;
break;
case MotionEvent.ACTION_CANCEL:
touched = false;
break;
case MotionEvent.ACTION_OUTSIDE:
touched = false;
break; default:
}
return true;
}
A minimal example with:
public class CustomButton extends ImageButton {
public CustomButton(Context context) {
super(context);
init();
}
public CustomButton(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public CustomButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
this.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Log.d(getClass().getSimpleName(), "ButtonPressed");
}
});
}
}
and they layout:
<com.example.bossb.test.CustomButton
android:layout_width="match_parent"
android:layout_height="match_parent" />
works fine for me.
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;
}
}