I have a problem using a SeekBar in my code...
I'd like to have a SeekBar which the values are set by me, not by the user. The only solution I've founded and I don't like, is setting enabled to false, but the colors of the seek bar become grey... (so I can't do anything when the user slides the thumb...)
I would get something like that, but without touchable events!
SOLVED:
I solved the problem, as #CommonsWare says:
public class TaskListProgressBar extends SeekBar {
public TaskListProgressBar(Context context) {
super(context);
}
public TaskListProgressBar(Context context, AttributeSet attrs) {
super(context, attrs);
}
public TaskListProgressBar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
return true;
}
#Override
public boolean onTrackballEvent(MotionEvent ev) {
return false;
}
You could subclass it and override onTouchEvent() and onTrackballEvent() (to eat all attempts to silde the thumb) and also use android:focusable="false" (to prevent it from getting the focus, so arrow keys cannot modify the thumb position).
That being said:
I have never tried this, so YMMV.
Please only do this if you are changing the default thumb image, per your screenshot above. Otherwise, users will expect the thumb to be movable, and they will get frustrated when they cannot move it.
Related
I'm a novice and this is my first Android app. I'm building a card game where the player should be able to tap to move a card (onClick) and also drag a card to a new location (onTouch).
I'm using CardViews for the cards.
I've built the setOnClickListener with no issues. I'm not having the same luck with the onTouchListener. I also really don't know if I should be using onTouch or onTouchListener...or maybe even onDragListener?
Based on YouTube and other StackOverflow posts, I've constructed a onTouchListener like so:
aceSpades.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
switch(event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
// do work
break;
case MotionEvent.ACTION_MOVE:
// do more work
break;
case MotionEvent.ACTION_UP:
// do final work
break;
}
}
}
This works fine, I'm able to move the card around OK within the game. However, it appears that a CardView cannot have both assigned:
setOnClickListener and setOnTouchListener
Also, in the onTouchListener the whole things is highlighted in yellow with the warning that it does not override the performClick.
I've tried to understand in more detail other explanations/solutions, but what I've read so far doesn't make much sense to me.
The most popular solution I have founded instructs the creation of a new package and custom view and let the custom view manage Overrides for onTouch and performClick(). I went as far as creating the file and added it to my xml, but the cardView disappears, so obviously something is wrong. I'll provide the code for that below.
I don't really follow how the custom view solution would work - it seems to need the performClick to be added to the ACTION_UP. For my app, the onClick is a set of different methods, separate from the methods I would include in ACTION_UP. Any assistance would be helpful, I'm really stuck here. Thanks.
Class File:
package views;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import androidx.annotation.Nullable;
public class CustomView extends View {
public CustomView(Context context) {
super(context);
}
public CustomView(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
}
public CustomView(Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public CustomView(Context context, #Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(event);
Log.d("Test", "onTouchEvent: Touched!");
switch(event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
performClick();
break;
}
return false;
}
#Override
public boolean performClick() {
super.performClick();
Log.d("Test", "performClick: clicked!");
return true;
}
}
XML:
<views.CustomView
android:id="#+id/test_drag_card"
android:layout_width="100px"
android:layout_height="250px"
android:backgroundTint="#color/standard_red"
android:visibility="visible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
As I shared above, when this code the card is not even visible when I launch the app. I'm testing on a real device. I'm using ConstraintLayout for my XML.
Please let me know if I need to provide any more details for a possible solution. Thanks.
My overall goal is to be able of dragging a row from the RecyclerView to another view inside my app.
With a standard RecyclerView with LinearLayoutmanager and no extra mumbo-jumbo my drag-and-drop operations work flawlessly. However, as soon as I introduce a custom library called AndroidSwipeLayout to make each row swipeable to reveal extra actions everything fails and I get the common error:
08-28 09:59:03.465: I/ViewRootImpl(15310): Reporting drop result: false
Also I can see that the only DragEvents that are fired on my receiving view are ACTION_DRAG_STARTED and ACTION_DRAG_ENDED, all other events are skipped. As you can see I am returning true from ACTION_DRAG_STARTED but that doesn't help, my thought is that the custom library somehow eats my event. But I can't pinpoint where.
Here is my OnDragListener:
private class MyDropListener implements View.OnDragListener {
#Override
public boolean onDrag(View v, DragEvent event) {
// Doing some calculations based on event x and y. Not related to the problem.
switch (event.getAction()) {
case DragEvent.ACTION_DRAG_STARTED:
// Some unrelated code, updating how views are displayed
return true;
case DragEvent.ACTION_DRAG_LOCATION:
// Some unrelated code, updating how views are displayed
return true;
case DragEvent.ACTION_DROP:
// Some unrelated code, updating some data and updating how views are displayed
return true;
case DragEvent.ACTION_DRAG_ENDED:
// Some unrelated code, updating how views are displayed
return true;
case DragEvent.ACTION_DRAG_ENTERED:
// Some unrelated code, updating how views are displayed
return true;
case DragEvent.ACTION_DRAG_EXITED:
// Some unrelated code, updating how views are displayed
return true;
default:
return false;
}
}
}
I've experimented now for a few days inside the library but can't find a solid solution exactly where my event dies. Sometimes I've managed to get the drop working, but it's very irregular behaviour.
I've also made a Github issue for this problem:
AndroidSwipeLayout - issue #211
I'm sure this is not specific to this library, but a problem when there's too much gesture detection going on for each view in a list. The library is really excellent I think and I don't wish to write that interaction myself.
Any thoughts, or comments, are welcome and appreciated. Even if you don't know the exact solution.
Thanks.
This was actually not caused by the library, or the listeners, it was caused by an EditText that was located in the same layout.
It was solved by creating a new class that subclasses EditText and ignores the dragEvent.
public class EditTextNoDrag extends EditText {
public EditTextNoDrag(Context context) {
super(context);
}
public EditTextNoDrag(Context context, AttributeSet attrs) {
super(context, attrs);
}
public EditTextNoDrag(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public EditTextNoDrag(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
#Override
public boolean onDragEvent(DragEvent event) {
switch (event.getAction()) {
case DragEvent.ACTION_DRAG_STARTED:
return false;
default:
return super.onDragEvent(event);
}
}
}
You can also toggle the focusable of the EditText on and off.
See this related isseu: Prevent drag drop of custom mimetype to EditText
i fought a long time with that problem without finding a solution anywhere.
I have a ListView that contains some RatingBars. Everytime i wanted to scroll and hit accidentially a ratingbar a rating was performed instead of a scroll.
Now I found a simple solution: I wrote my own RatingBar:
public class ScrollableRatingBar extends RatingBar {
public ScrollableRatingBar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public ScrollableRatingBar(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ScrollableRatingBar(Context context) {
super(context);
}
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
final int action = ev.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
return true;
}
case MotionEvent.ACTION_MOVE: {
return true;
}
}
return super.dispatchTouchEvent(ev);
}
}
What happens: if you click at a RatingBar there are 3 different MotionEvents:
-Action_DOWN: Put your finger down
-Action_MOVE: Move your finger
-Action_UP: Put your finger up
You don't want to set the rating at the action "down" or "move". So just return true to signal it has happend. If "up" comes, the programm differes itself if it is a scroll or a rating, depending what you did.
That's all!
I hope someone helps that.
EDIT:
I realize that it is a problem of Android 2.3.7 and lower.
I want to override the back button when the soft keyboard is shown. Basically when the back button is hit, I want the keyboard to dismiss, and I want to append some text onto whatever the user has typed in that edit text field. So basically I need to know when the keyboard is dismissed. After searching around, I realized there is no API for this, and that the only real way to do this would be to make your EditText class.
So I created my own EditText class and extended EditText like this
public class CustomEditText extends EditText
{
public CustomEditText(Context context)
{
super(context);
init();
}
public CustomEditText(Context context, AttributeSet attrs)
{
super(context, attrs);
init();
}
public CustomEditText(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
init();
}
private void init()
{
}
}
I have also added this method
#Override
public boolean dispatchKeyEventPreIme(KeyEvent event)
{
if (KeyEvent.KEYCODE_BACK == event.getKeyCode())
{
Log.v("", "Back Pressed");
//Want to call this method which will append text
//init();
}
return super.dispatchKeyEventPreIme(event);
}
Now this method does override the back button, it closes the keyboard, but I dont know how I would pass text into the EditText field. Does anyone know how I would do this?
Also another quick question, does anyone know why this method is called twice? As you can see for the time being, I have added a quick logcat message to test it works, but when I hit the back button, it prints it twice, any reason why it would be doing this?
Any help would be much appreciated!!
This is due to the dispatchKeyEventPreIme being called on both ACTION_DOWN and ACTION_UP.
You will have to process only when KEY down is pressed. So use
if(event.getAction () == KeyEvent.ACTION_DOWN)
Edit:
for the first question You could do
setText(getText().toString() + " whatever you want to append");
in dispatchKeyEventPreIme
Why twice? Probably the method is called on press down and up event.
Is there a way of disabling panning/zooming and keeping map overlays clickable?
I am specifically thinking about an ItemizedOverlay that I want to be tappable while denying users from moving around the map's viewport (It's for a game).
I've seen the same question answered but the answer sadly doesn't allow for overlay items to be clicked.
Thanks alot,
best regards,
Alex
I know this question is a little old but I had the same problem and found a workaround, it's not a graceful solution but appears to work.
First you have to create your own map subclass overriding the onTouchEvent handler
public class NonPannableMapView extends MapView {
public NonPannableMapView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public NonPannableMapView(Context context, AttributeSet attrs){
super(context, attrs);
}
public NonPannableMapView(Context context, String apiKey) {
super(context, apiKey);
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
//Handle a move so there is no panning or zoom
if(ev.getAction() == MotionEvent.ACTION_MOVE)
return true;
return super.onTouchEvent(ev);
}
}
Then it's just a case of changing the MapView reference in your layout to NonPannableMapView.
Try this:
mapView.setBuiltInZoomControls(false);
Hope that works! Have fun!