I'm trying to make a reusable component that slides its content.
Here is the code of the component :
package com.example.components;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.LinearLayout;
public class SlideRightNLeft extends LinearLayout{
private LinearLayout.LayoutParams params1;
private LinearLayout.LayoutParams params2;
public SlideRightNLeft(Context context) {
super(context);
}
public SlideRightNLeft(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SlideRightNLeft(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public void slideRight(){
int x = 0;
while(x < 300) {
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
params1 = new LinearLayout.LayoutParams(this.getChildAt(0).getWidth(), this.getChildAt(0).getHeight());
params1.setMargins(300 - x, 1, 1, 1);
params2 = new LinearLayout.LayoutParams(this.getChildAt(0).getWidth(), this.getChildAt(0).getHeight());
params2.setMargins(x, 1, 1, 1);
this.getChildAt(0).setLayoutParams(params1);
this.getChildAt(1).setLayoutParams(params2);
x++;
this.invalidate();
}
}
}
Unfortunately it won't slide. In can suppose I have to use runOnUiThread to actually see it slide, but it needs an activity, and as I am in the component, I don't have access to it.
You can use the Context thats passed into the constructor to access the runOnUiThread method.
((Activity)context).runOnUiThread(new Runnable(){
});
Related
I am getting the error: "Cannot resolve method 'super()'" in my code and I am not sure how to resolve this, do you have any clues?
The code is as follows:
public GeoView( double left, double top, double width )
{
super();
this.left = left;
this.top = top;
this.width = width;
this.transform = null;
this.backing_store = null;
this.sink = false;
this.last_size = new Rect(0, 0, 200, 200 );
this.do_tracking = false;
Drawable background = new Drawable() {
#Override
public void draw(Canvas canvas) {
}
#Override
public void setAlpha(int i) {
}
#Override
public void setColorFilter(ColorFilter colorFilter) {
}
#Override
public int getOpacity() {
return 0;
}
};
What is GeoView class? You extended it from another class?
If yes. Check that parent class have empty constructor, because you call it.
If it's your own class and it didn't extended from any other class. Then you don't need to call super().
View doesn't have empty constructor.
You should Have at least one of this constructor.
public GeoView(Context context) {
super(context);
}
public GeoView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public GeoView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
If you want to create view by yourself, you need first constructor. Then you have to
GeoView geoView = new GeoView(YourActivity.this);
Or you can simply add your view to layout. Then You second will be called constructor. I think you should add all of them.
<com.example.yourapp.GeoView
android:layout_height="300dp"
android:layout_width="match_parent"/>
I have a custom ImageView class (I don't think the code in the class matters but I'll include it here anyway incase it is needed):
package com.example.vroy.customcirclebuttontest;
import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.ImageView;
public class CircleButton extends ImageView {`
private int radius;
private int x;
private int y;
public CircleButton(Context context) {
super(context);
constructorTask();
}
public CircleButton(Context context, AttributeSet attrs) {
super(context, attrs);
constructorTask();
}
public CircleButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
constructorTask();
}
public CircleButton(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
constructorTask();
}
private void constructorTask() {
x = 300;
y = 300;
radius = 100;
}
#Override
public void setPressed(boolean pressed) {
super.setPressed(pressed);
Log.i("Button Logger","Button Pressed");
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawCircle(x, y, radius, GameView.green);
Log.i("Drawing status", "CircleButton Drawing..." + " " + Integer.toString(this.x) + "," + Integer.toString(this.y));
}
}
I declare a instance of this class in my activity:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
testCircleButton = new CircleButton(getApplicationContext());
makeFullScreen();
testCircleButton.getLayoutParams().width = 200;
testCircleButton.getLayoutParams().height = 200;
RelativeLayout screenLayout = (RelativeLayout) findViewById(R.id.screenLayout);
screenLayout.addView(testCircleButton);
Log.i("Button Status","Adding Button To Layout");
}
However when I do this I get a "null pointer" exception.
How can I fix this?
It's because your testCircleButton is not attached to the Activity. It has no LayoutParams if it's not added to any parent.
Use
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(200, 200) // I'm not sure about constructor arguments.
testCircleButton.setLayoutParams(lp);
Use different LayoutParams if your root layout is different(FrameLayout etc).
I've overridden ScrollView to pass MotionEvents to a GestureDetector to detect fling events on the ScrollView. I need to be able to detect when the scrolling stops. This doesn't coincide with the MotionEvent.ACTION_UP event because this usually happens at the start of a fling gesture, which is followed by a flurry of onScrollChanged() calls on the ScrollView.
So basically what we are dealing with here is the following events:
onFling
onScrollChanged, onScrollChanged, onScrollChanged, ... , onScrollChanged
There's no callback for when the onScrollChanged events are done firing. I was thinking of posting a message to the event queue using a Handler during onFling and waiting for the Runnable to execute to signal the end of the fling, unfortunately it fires after the first onScrollChanged call.
Any other ideas?
I've combined a few of the answers from here to construct a working listener that resembles the way AbsListView does it. It's essentially what you describe, and it works well in my testing.
Note: you can simply override ScrollView.fling(int velocityY) rather than use your own GestureDetector.
import android.content.Context;
import android.util.AttributeSet;
import android.widget.ScrollView;
public class CustomScrollView extends ScrollView {
private static final int DELAY_MILLIS = 100;
public interface OnFlingListener {
public void onFlingStarted();
public void onFlingStopped();
}
private OnFlingListener mFlingListener;
private Runnable mScrollChecker;
private int mPreviousPosition;
public CustomScrollView(Context context) {
this(context, null, 0);
}
public CustomScrollView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CustomScrollView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mScrollChecker = new Runnable() {
#Override
public void run() {
int position = getScrollY();
if (mPreviousPosition - position == 0) {
mFlingListener.onFlingStopped();
removeCallbacks(mScrollChecker);
} else {
mPreviousPosition = getScrollY();
postDelayed(mScrollChecker, DELAY_MILLIS);
}
}
};
}
#Override
public void fling(int velocityY) {
super.fling(velocityY);
if (mFlingListener != null) {
mFlingListener.onFlingStarted();
post(mScrollChecker);
}
}
public OnFlingListener getOnFlingListener() {
return mFlingListener;
}
public void setOnFlingListener(OnFlingListener mOnFlingListener) {
this.mFlingListener = mOnFlingListener;
}
}
Thank you #PaulBurke +1
Xamarin Solution
using Android.Content;
using Android.Runtime;
using Android.Util;
using Android.Widget;
using System;
public class CustomScrollView : ScrollView
{
public event EventHandler FlingEnded;
public event EventHandler FlingStarted;
private Action ScrollChecker;
private int PreviousPosition;
private const int DELAY_MILLIS = 100;
public CustomScrollView(Context context) : base(context) => Init();
public CustomScrollView(Context context, IAttributeSet attrs) : base(context, attrs) => Init();
public CustomScrollView(Context context, IAttributeSet attrs, int defStyleAttr) : base(context, attrs, defStyleAttr) => Init();
public CustomScrollView(Context context, IAttributeSet attrs, int defStyleAttr, int defStyleRes) : base(context, attrs, defStyleAttr, defStyleRes) => Init();
public CustomScrollView(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer) { }
private void Init()
{
ScrollChecker = () =>
{
int position = ScrollY;
if (PreviousPosition - position == 0)
{
FlingEnded?.Invoke(this, new EventArgs());
RemoveCallbacks(ScrollChecker);
}
else
{
PreviousPosition = ScrollY;
PostDelayed(ScrollChecker, DELAY_MILLIS);
}
};
}
public override void Fling(int velocityY)
{
base.Fling(velocityY);
FlingStarted?.Invoke(this, new EventArgs());
Post(ScrollChecker);
}
}
Usage:
myCustomScrollView.FlingEnded += myCustomScrollView_FlingEnded;
protected void myCustomScrollView_FlingEnded(object sender, EventArgs e) =>
{
//Do onFlingEnded code here
};
I'm using the new SlidingPaneLayout available in the latest support library, which provides the openPane() and closePane() methods to smooth open and close the panel. Unfortunately, there are no public methods to do so without animation.
Is there a way to still do this? I have a feeling reflection may be necessary.
P.S. The file is available under sdk/extras/android/support/v4/src/java/android/support/v4/widget/.
I ended up writing a subclass that provides two methods, openPaneNoAnimation() and closePaneNoAnimation(). Yes, it's reflection, and may stop working with future support libraries, but for now it does the job. Worst case, the methods fall back to using openPane() and closePane().
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import android.content.Context;
import android.support.v4.widget.SlidingPaneLayout;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
public class AnimationlessSlidingPaneLayout extends SlidingPaneLayout {
private boolean mSlideEnabled = true;
private Field mSlideOffsetField = null;
private Field mSlideableViewField = null;
private Method updateObscuredViewsVisibilityMethod = null;
private Method dispatchOnPanelOpenedMethod = null;
private Method dispatchOnPanelClosedMethod = null;
private Field mPreservedOpenStateField = null;
private Method parallaxOtherViewsMethod = null;
public AnimationlessSlidingPaneLayout(Context context) {
this(context, null, 0);
}
public AnimationlessSlidingPaneLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public AnimationlessSlidingPaneLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
try {
mSlideOffsetField = SlidingPaneLayout.class.getDeclaredField("mSlideOffset");
mSlideableViewField = SlidingPaneLayout.class.getDeclaredField("mSlideableView");
updateObscuredViewsVisibilityMethod = SlidingPaneLayout.class.getDeclaredMethod("updateObscuredViewsVisibility",
View.class);
dispatchOnPanelClosedMethod = SlidingPaneLayout.class.getDeclaredMethod("dispatchOnPanelClosed", View.class);
dispatchOnPanelOpenedMethod = SlidingPaneLayout.class.getDeclaredMethod("dispatchOnPanelOpened", View.class);
mPreservedOpenStateField = SlidingPaneLayout.class.getDeclaredField("mPreservedOpenState");
parallaxOtherViewsMethod = SlidingPaneLayout.class.getDeclaredMethod("parallaxOtherViews", float.class);
mSlideOffsetField.setAccessible(true);
mSlideableViewField.setAccessible(true);
updateObscuredViewsVisibilityMethod.setAccessible(true);
dispatchOnPanelOpenedMethod.setAccessible(true);
dispatchOnPanelClosedMethod.setAccessible(true);
mPreservedOpenStateField.setAccessible(true);
parallaxOtherViewsMethod.setAccessible(true);
} catch (Exception e) {
Log.w("ASPL", "Failed to set up animation-less sliding layout.");
}
}
public void openPaneNoAnimation() {
try {
View slideableView = (View) mSlideableViewField.get(this);
mSlideOffsetField.set(this, 1.0f);
parallaxOtherViewsMethod.invoke(this, 1.0f);
requestLayout();
invalidate();
dispatchOnPanelOpenedMethod.invoke(this, slideableView);
mPreservedOpenStateField.set(this, true);
} catch (Exception e) {
openPane();
}
}
public void closePaneNoAnimation() {
try {
View slideableView = (View) mSlideableViewField.get(this);
mSlideOffsetField.set(this, 0.0f);
parallaxOtherViewsMethod.invoke(this, 0.0f);
requestLayout();
invalidate();
updateObscuredViewsVisibilityMethod.invoke(this, slideableView);
dispatchOnPanelClosedMethod.invoke(this, slideableView);
mPreservedOpenStateField.set(this, false);
} catch (Exception e) {
closePane();
}
}
}
I want to do a custom horizontal navigation bar, which consists of as many dots as i have pages.
My thoughts are:
Create a Custom ListView
Create Class Dot extends from View
Add all these dots to the custom listview dynamically..
Is this right to do so?
EDIT:
public class NavigationBarLesson extends LinearLayout {
private LessonConfig config = LessonConfig.getInstance();
private ArrayList<NavigationCircle> navigationCircles;
private int pageCount;
public NavigationBarLesson(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public NavigationBarLesson(Context context) {
super(context);
init(context);
}
private void init(Context context) {
Log.i("init","yes");
pageCount = config.getLektionCount();
navigationCircles = new ArrayList<NavigationCircle>();
for(int i=0; i < pageCount; i++){
this.addView(new NavigationCircle(context));
}
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = View.MeasureSpec.getSize(widthMeasureSpec);
int height = View.MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(width, height);
}
}
public class NavigationCircle extends ImageView{
private static Bitmap img;
private Bitmap activeImg;
public NavigationCircle(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
public NavigationCircle(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public NavigationCircle(Context context) {
super(context);
init();
}
public void init() {
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
lp.setMargins(0, 0, 5, 0);
this.setLayoutParams(lp);
this.setBackgroundResource(R.drawable.upcoming_pages);
}
public Bitmap getImg() {
return img;
}
public Bitmap getActiveImg() {
return this.activeImg;
}
}
Well, I do not see why you would need a ListView. Create a horizontal LinearLayout with some simple views like TextView's or ImageView's etc added dynamically to the LinearLayout.