Custom scrollview doesn't scroll - java

I have a problem. I have my own scrollview, but it doesn't work. The scrollbar appears, but when I try to use it doesn't work. I have tried a lot of things, but none work. The code is:
#SuppressLint("ClickableViewAccessibility") public class myScrollView extends ScrollView {
private boolean enableScrolling = true;
public boolean isEnableScrolling() {
return enableScrolling;
}
public void setEnableScrolling(boolean enableScrolling) {
this.enableScrolling = enableScrolling;
}
public myScrollView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public myScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public myScrollView(Context context) {
super(context);
}
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (isEnableScrolling()) {
return super.onInterceptTouchEvent(ev);
} else {
return false;
}
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
if (isEnableScrolling()) {
return super.onInterceptTouchEvent(ev);
} else {
return false;
}
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// TODO Auto-generated method stub
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(getMeasuredWidth(), getMeasuredHeight());
}
}
Inside of this scroll, I have a relativeLayout and inside of this a View. In the View I draw some bitmaps. They number more than it can display the screen.
The XML is:
<com.edjume.myScrollView
android:id="#+id/scroll_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="#+id/table">
<RelativeLayout
android:id="#+id/table"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
</RelativeLayout>
</com.edjume.myScrollView>
And in the activity I use:
RelativeLayout ll = (RelativeLayout) findViewById(R.id.table);
ll.addView(new Table(this));
And finally, in my View (Table) has a OnDraw() and a onTouchEvent(). In the onTouchEvent I use the options ACTION_DOWN, ACTION_MOVE and ACTION_UP.

I believe
#Override
public boolean onTouchEvent(MotionEvent ev) {
if (isEnableScrolling()) {
return super.onInterceptTouchEvent(ev);
} else {
return false;
}
}
should be:
#Override
public boolean onTouchEvent(MotionEvent ev) {
if (isEnableScrolling()) {
return super.onTouchEvent(ev);
} else {
return false;
}
}

Related

Android MPAndroidChart LineChart how to Ignore ViewPager touch gestures completely?

I am trying to make the linechart from mpandroidchart ignore the fragmentviewpager and only move the linechart and not the viewpager when touching the view. My question is how can I archive this?
I found this solution but dont know how to implement it Maybee you guys can help me understand how to implement it?
https://github.com/PhilJay/MPAndroidChart/issues/1885#issuecomment-267568663
public class PagerAdapter extends FragmentStatePagerAdapter {
int mNumberOfTabs;
public PagerAdapter(FragmentManager fm, int NumberOfTabs) {
super(fm);
this.mNumberOfTabs = NumberOfTabs;
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
IncomeOutcomeOverviewFragment incomeOutcomeOverviewFragment = new IncomeOutcomeOverviewFragment();
return incomeOutcomeOverviewFragment;
case 1:
IncomeFragment incomeFragment = new IncomeFragment();
return incomeFragment;
case 2:
OutcomeFragment outcomeFragment = new OutcomeFragment();
return outcomeFragment;
default:
return null;
}
}
#Override
public int getCount() {
return mNumberOfTabs;
}
}
I think what you want is nor swipeable view pager for that you have to create custom view pager like this
UPDATE changes suggested by #anomynous
public class NonSwipeableViewPager extends ViewPager {
boolean enabled = false;
public NonSwipeableViewPager(Context context) {
super(context);
setMyScroller();
}
public NonSwipeableViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
setMyScroller();
}
#Override
public boolean onInterceptTouchEvent(MotionEvent event) {
if (this.enabled) {
return false;
}
return super.onInterceptTouchEvent(event);
}
#SuppressLint("ClickableViewAccessibility")
#Override
public boolean onTouchEvent(MotionEvent event) {
if (this.enabled) {
return false;
}
return super.onTouchEvent(event);
}
//down one is added for smooth scrolling
private void setMyScroller() {
try {
Class<?> viewpager = ViewPager.class;
Field scroller = viewpager.getDeclaredField("mScroller");
scroller.setAccessible(true);
scroller.set(this, new MyScroller(getContext()));
} catch (Exception e) {
e.printStackTrace();
}
}
public class MyScroller extends Scroller {
public MyScroller(Context context) {
super(context, new DecelerateInterpolator());
}
#Override
public void startScroll(int startX, int startY, int dx, int dy, int duration) {
super.startScroll(startX, startY, dx, dy, 350 /*1 secs*/);
}
}
public void setPagingDisabled(boolean enabled) {
this.enabled = enabled;
}
}
Then on my linechart I used onChartGestureStart and onChartGestureEnd to enable or disable the pageview:
#Override
public void onChartGestureStart(MotionEvent me, ChartTouchListener.ChartGesture lastPerformedGesture) {
nonSwipeableViewPager.setPagingDisabled(true);
}
#Override
public void onChartGestureEnd(MotionEvent me, ChartTouchListener.ChartGesture lastPerformedGesture) {
nonSwipeableViewPager.setPagingDisabled(false);
}
replace default viewpager with custom one in xml
You need you to disable touch for your chart, you can use this code,
chart.setTouchEnabled(true);

How to prevent click on other tab in NonScrollableViewPager?

I disable swipe the ViewPager with TabLayout as below:
public class NonScrollableViewPager extends ViewPager {
public NonScrollableViewPager(Context context) {
super(context);
init(context, null);
}
public NonScrollableViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
}
#Override
public boolean onInterceptTouchEvent(MotionEvent event) {
// Never allow swiping to switch between pages
return false;
}
#Override
public boolean onTouchEvent(MotionEvent event) {
// Never allow swiping to switch between pages
return false;
}
}
How to prevent other tabs clickable when select one of tabs, then computation complected then enable the clickable. ViewPager.setClickable(false) and setEnabled() don't work
disable clickable
// do something
enable clickable
How to do that?
I am assuming that you are using TabLayout along with ViewPager. So, you need to implement as follows
LinearLayout tabStrip = ((LinearLayout) your_tab_layout.getChildAt(0));
for(int i = 0; i < tabStrip.getChildCount(); i++) {
tabStrip.getChildAt(i).setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
return true;
}
});
}
For API 24 and above
your_tab_layout.clearOnTabSelectedListeners();
Hope this helps

How to add delay in onDraw() in android canvas?

I am making a project. It draws concentric circles in android canvas. When the user drags the screen, all the circles move accordingly. Here is my code so far.
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:background="#android:color/white">
<RelativeLayout
android:id="#+id/scrollableSpace"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true">
<project.myProject.DrawOrbit
android:id="#+id/orbitsRegion"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true" />
</RelativeLayout>
</RelativeLayout>
MainActivity.java
public class MainActivity extends AppCompatActivity implements View.OnTouchListener
{
PointF center;
center.x=500;center.y=500;
float radius[]={100,200,300,400,500};
DrawOrbit orbit;
int startX=0,startY=0,currentX=0,currentY=0;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
RelativeLayout layout=(RelativeLayout)findViewById(R.id.scrollableSpace);
orbit.draw(center,radius);
layout.setOnTouchListener(this);
}
#Override
public boolean onTouch(View v, MotionEvent motionEvent)
{
final int action= motionEvent.getAction();
switch(action & MotionEvent.ACTION_MASK)
{
case MotionEvent.ACTION_DOWN:
{
startX=(int)motionEvent.getRawX();
startY=(int)motionEvent.getRawY();
break;
}
case MotionEvent.ACTION_MOVE:
{
currentX=(int)motionEvent.getRawX();
currentY=(int)motionEvent.getRawY();
float diffX=currentX-startX;
float diffY=currentY-startY;
startX=currentX;
startY=currentY;
center.x+=diffX;
center.y+=diffY;
orbit.draw(center,radius);
break;
}
}
return true;
}
}
DrawOrbit.java
public class DrawOrbit extends View
{
PointF center;
float radius[];
public DrawOrbit(Context context) {
super(context);
}
public DrawOrbit(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
}
public DrawOrbit(Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
paint.setColor(Color.BLACK);
paint.setStrokeWidth(2);
paint.setStyle(Paint.Style.STROKE);
int len=radius.length;
for(int a=0;a<len;a++)
{
canvas.drawCircle(center.x,center.y,radius[a],paint);
}
}
public void draw(PointF center, float radius[])
{
this.center=center;
this.radius=radius;
invalidate();
requestLayout();
}
}
What I want to do is that the circles should appear one by one. First the inner most circle then the next one after some delay then the next and so on. The same effect should be seen when the screen is dragged. How can I achieve this? Any help will be appreciated.
What you're looking for is a timeout. I'd suggest creating a new thread to draw everything, and start by drawing the first circle, have the method look like this:
public void drawCircle() {
//Draw logic
TimeUnit.SECONDS.sleep(3);
drawNextCircle();
}
//Assuming on java 1.8+
Thread thread = new Thread() => {
drawCircle();
}
What this will do is have the thread sleep for 3 seconds, and then continue normal operation after the time period is up. You can change it to other measurements of time such as TimeUnit.MILLISECONDS or TimeUnit.MINUTES as well.
edit: You likely won't want this in your main thread, as it will stop the ENTIRE application from working for however long you put the thread on timeout, so it's almost essential for this to work that you put it in a separate thread.
edit 2: It would make more sense to add a separate util method to timeout and then call another method via reflection than the above code, but the same code would be needed.
Figured out the answer to my own question. I had to use a Handler, multiplied the delay with the for-loop variable and made 1 then 2 then so on.. circles to get the desired effect. Here is the code.
MainActivity.java
public class MainActivity extends AppCompatActivity implements View.OnTouchListener
{
PointF center;
center.x=500;center.y=500;
float radius[]={100,200,300,400,500};
DrawOrbit orbit;
int startX=0,startY=0,currentX=0,currentY=0;
Handler handler1 = new Handler();
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
RelativeLayout layout=(RelativeLayout)findViewById(R.id.scrollableSpace);
for (int a = 0; a<radius.length ;a++)
{
final int index=a;
handler1.postDelayed(new Runnable() {
#Override
public void run() {
orbit.draw(center,radius,index);
}
}, 300 * a);
}
layout.setOnTouchListener(this);
}
#Override
public boolean onTouch(View v, MotionEvent motionEvent)
{
final int action= motionEvent.getAction();
switch(action & MotionEvent.ACTION_MASK)
{
case MotionEvent.ACTION_DOWN:
{
startX=(int)motionEvent.getRawX();
startY=(int)motionEvent.getRawY();
break;
}
case MotionEvent.ACTION_MOVE:
{
currentX=(int)motionEvent.getRawX();
currentY=(int)motionEvent.getRawY();
float diffX=currentX-startX;
float diffY=currentY-startY;
startX=currentX;
startY=currentY;
center.x+=diffX;
center.y+=diffY;
break;
}
case MotionEvent.ACTION_UP:
{
for (int a = 0; a<radius.length ;a++)
{
final int index=a;
handler1.postDelayed(new Runnable() {
#Override
public void run() {
orbit.draw(center,radius,index);
}
}, 300 * a);
}
break;
}
}
return true;
}
}
DrawOrbit.java
public class DrawOrbit extends View
{
PointF center;
float radius[];
int index;
public DrawOrbit(Context context) {
super(context);
}
public DrawOrbit(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
}
public DrawOrbit(Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
paint.setColor(Color.BLACK);
paint.setStrokeWidth(2);
paint.setStyle(Paint.Style.STROKE);
int len=radius.length;
for(int a=0;a<index;a++)
{
canvas.drawCircle(center.x,center.y,radius[a],paint);
}
}
public void draw(PointF center, float radius[],int index)
{
this.center=center;
this.radius=radius;
this.index=index;
invalidate();
requestLayout();
}
}

Android: Restrict a user from swiping screens in ViewPager

I have a Tabbed Activity: a user can swipe to the right or to the left to change the screens. At some point I want to record an audio and while it is being recorder the user is not supposed to change the screens.
Question: How to restrict a user from changing screens (swiping) in ViewPager?
I tried to figure out how to do that using ViewPager API but I didn't manage to find a proper way (there are hundreds or may be even thousand methods in ViewPager none of which is likely to do what I want).
You can extend the ViewPager class and use this code.
public class LockableViewPager extends ViewPager {
private boolean swipeable;
public LockableViewPager(Context context) {
super(context);
}
public LockableViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
this.swipeable = true;
}
#Override
public boolean onInterceptTouchEvent(MotionEvent event) {
return this.swipeable && super.onInterceptTouchEvent(event);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
return this.swipeable && super.onTouchEvent(event);
}
public void setSwipeable(boolean swipeable) {
this.swipeable = swipeable;
}
}
Then in your Fragment or Activity you can just call mViewPager.setSwipeable(false); to make it unswipeable.
Create a custom viewPager like below
public class CustomViewPager extends ViewPager {
private boolean enabled = true;
public CustomViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
enabled = true;
}
#Override
public boolean onInterceptTouchEvent(MotionEvent arg0) {
if (enabled) {
return super.onInterceptTouchEvent(arg0);
} else {
return false;
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (this.enabled) {
return super.onTouchEvent(event);
}
return false;
}
public void setPagingEnabled(boolean enabled) {
this.enabled = enabled;
}
}
Then simply call viewPager.setPagingEnabled(false);

Why is my custom spinner disabled/unresponsive?

I've created a custom spinner, because again and again, I found that I wanted to make sure that the onItemSelectedListener wasn't triggered when I set my Spinner's initial selection or set a new custom adapter. I only want it triggered when a user actually selects an item.
But for some reason (I'm at a complete loss as to why), my custom spinner doesn't respond to touch events. It's as if it's disabled, even though I've debugged and seen that it's perfectly enabled. But for some reason, my little spinner won't open. Can anyone help me understand why?
Here's the xml:
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginTop="#dimen/default_margin"
android:orientation="horizontal">
<my.app.custom.view.MySpinner
android:id="#+id/dog_or_cat_toggle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2"
android:layout_margin="0dp"
android:textAlignment="center"
android:gravity="center_vertical|center"
android:padding="0dp"
android:entries="#array/dog_or_cat"
android:spinnerMode="dropdown"
android:background="#drawable/top_to_bottom_gray_gradient"/>
...
</LinearLayout>
And my Custom Spinner:
/* A Spinner dispatches an onItemSelected event when the View is initialized, before the user ever makes a selection.
* This class allows listeners for just the initial selection, just user selections, or both. */
public class MySpinner extends Spinner {
private boolean initialized = false;
private OnItemSelectedListener onItemSelectionInitializedListener;
private OnItemSelectedListener onItemSelectedByUserListener;
private OnItemSelectedListener onItemSelectedListener;
public MySpinner(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MySpinner(Context context, AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
public MySpinner(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
this.initializeMySpinner();
}
public void setOnItemSelectionInitializedListener(OnItemSelectedListener onItemSelectionInitializedListener) {
this.onItemSelectionInitializedListener = onItemSelectionInitializedListener;
}
public void setOnItemSelectedByUserListener(OnItemSelectedListener onItemSelectedByUserListener) {
this.onItemSelectedByUserListener = onItemSelectedByUserListener;
}
#Override
public void setOnItemSelectedListener(OnItemSelectedListener onItemSelectedListener) {
this.onItemSelectedListener = onItemSelectedListener;
}
#Override
public void setAdapter(SpinnerAdapter adapter) {
this.initialized = false;
super.setAdapter(adapter);
}
private void initializeMySpinner() {
super.setOnItemSelectedListener(new OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
if(!initialized) {
if(onItemSelectionInitializedListener != null) onItemSelectionInitializedListener.onItemSelected(parent, view, position, id);
if(onItemSelectedListener != null) onItemSelectedListener.onItemSelected(parent, view, position, id);
initialized = true;
} else {
if(onItemSelectedListener != null) onItemSelectedListener.onItemSelected(parent, view, position, id);
if(onItemSelectedByUserListener != null) onItemSelectedByUserListener.onItemSelected(parent, view, position, id);
}
}
#Override
public void onNothingSelected(AdapterView<?> parent) {
if(!initialized) {
if(onItemSelectionInitializedListener != null) onItemSelectionInitializedListener.onNothingSelected(parent);
if(onItemSelectedListener != null) onItemSelectedListener.onNothingSelected(parent);
initialized = true;
} else {
if(onItemSelectedListener != null) onItemSelectedListener.onNothingSelected(parent);
if(onItemSelectedByUserListener != null) onItemSelectedByUserListener.onNothingSelected(parent);
}
}
});
}
}
Don't call one constructor from another. Instead, call super() constructor from each one.
I have faced the same issue some time back and this trick worked, but I'm not sure about the reason.

Categories