I am working on a text editor for a programming language, it has two parts, the line number box (2) and the code box (1) like in the figure below
..................
. . .
. . .
. 2. 1 .
. . .
. . .
. . .
..................
If the user clicks in the line number box (2), I need to execute some code, here I just show a toast with the message 'Click' but ignoure the default behavior of edit text.
If the user clicks inside box 1, just delegate the default edittext behavior by calling super.onTouchEvent(event).
Here's what I did, but I don't know how to get it to work properly, i need to play with the return value of onTouchEvent:
public class MyEditText extends AppCompatEditText {
private Rect drawingBound;
private Rect leftRect; // area 2
private Paint rectPaint;
private static final int PADDING = 100;
private boolean down = false;
public MyEditText(#NonNull Context context) {
super(context);
init(context);
}
public MyEditText(#NonNull Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
init(context);
}
public MyEditText(#NonNull Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private void init(Context context) {
setPadding(PADDING, 0, 0, 0);
drawingBound = new Rect();
leftRect = new Rect();
rectPaint = new Paint();
rectPaint.setColor(Color.GREEN);
rectPaint.setStyle(Paint.Style.STROKE);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
getDrawingRect(drawingBound);
leftRect.set(drawingBound);
leftRect.right = drawingBound.left+ PADDING;
canvas.drawRect(leftRect, rectPaint);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getActionMasked();
if (action == MotionEvent.ACTION_DOWN) {
down = true;
return false;
}
else if (action == MotionEvent.ACTION_UP) {
if (down) {
down = false;
int x = (int) event.getX();
int y = (int) event.getY();
if (leftRect.contains(x, y)) {
Toast.makeText(getContext(), "Click", Toast.LENGTH_SHORT).show();
return true;
}
}
return super.onTouchEvent(event);
} else {
return super.onTouchEvent(event);
}
}
}
Related
I need to customize default android seekbar to control music player. I know this sounds very simple, but I just don't know how to set up seekbar thumb listener. I want to control music and change icon accordingly to play and pause when user press on seekbar thumb icon. How can I achieve this? I know that this is possible because I previously saw apps like PocketGuide where this functionality is implemented. Here's the screenshot from PocketGuide app
Maybe this helps you. Adjust the code for your needs.
public class SeekbarWithThumbTouch extends SeekBar {
private int scaledTouchSlop = 0;
private float initTouchX = 0;
private boolean thumbPressed = false;
public SeekbarWithThumbTouch(Context context) {
super(context);
init(context);
}
public SeekbarWithThumbTouch(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public SeekbarWithThumbTouch(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private void init(Context context) {
scaledTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
}
#Override
public boolean onTouchEvent(MotionEvent event) {
Drawable thumb = null;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
thumb = getThumb();//works only for API >=16!
if (thumb != null) {
//contains current position of thumb in view as bounds
RectF bounds = new RectF(thumb.getBounds());
thumbPressed = bounds.contains(event.getX(), event.getY());
if (thumbPressed) {
Log.d("Thumb", "pressed");
initTouchX = event.getX();
return true;
}
}
break;
case MotionEvent.ACTION_UP:
if (thumbPressed) {
Log.d("Thumb", "was pressed -- listener call");
thumbPressed = false;
}
break;
case MotionEvent.ACTION_MOVE:
if (thumbPressed) {
if (Math.abs(initTouchX - event.getX()) > scaledTouchSlop) {
initTouchX = 0;
thumbPressed = false;
return super.onTouchEvent(event);
}
Log.d("Thumb", "move blocked");
return true;
}
break;
}
return super.onTouchEvent(event);
}
}
I need to display an array of dots (ImageView) that behave like the RatingBar, here's an example:
This is pretty much an RatingBar rotated, but I've a problem, this application is pixel-perfect and therefore I need to add some margin between the dots. This cannot be done with an RatingBar. With all this issues that I'm facing trying to use the RatingBar I gave up and I decided to make my own component, so far this is the component:
public class DotContainerView extends LinearLayout {
#InjectView(R.id.view_dot_container)
LinearLayout vDotContainer;
private OnRatingBarChangeListener mListener;
public DotContainerView(Context context) {
super(context);
initialize();
}
public DotContainerView(Context context, AttributeSet attrs) {
super(context, attrs);
initialize();
}
public DotContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initialize();
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public DotContainerView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
initialize();
}
private void initialize() {
View root = LayoutInflater.from(getContext()).inflate(R.layout.view_dot_container, this);
ButterKnife.inject(this, root);
setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
v.onTouchEvent(event);
return false;
}
});
}
public void setRating(int rating) {
for (int index = 0; index < rating; index++) {
vDotContainer.getChildAt(index).setSelected(false);
}
for (int index = rating; index < 10; index++) {
vDotContainer.getChildAt(index).setSelected(true);
}
}
public void setOnRatingBarChangeListener(DotContainerView.OnRatingBarChangeListener listener) {
mListener = listener;
}
//region OnTouch
#OnTouch(R.id.fragment_brightness_control_dot_1)
public boolean onDot1Touched() {
setRating(1);
mListener.onRatingChanged(this, 1, true);
return true;
}
#OnTouch(R.id.fragment_brightness_control_dot_2)
public boolean onDot2Touched() {
setRating(2);
mListener.onRatingChanged(this, 2, true);
return true;
}
#OnTouch(R.id.fragment_brightness_control_dot_3)
public boolean onDot3Touched() {
setRating(3);
mListener.onRatingChanged(this, 3, true);
return true;
}
#OnTouch(R.id.fragment_brightness_control_dot_4)
public boolean onDot4Touched() {
setRating(4);
mListener.onRatingChanged(this, 4, true);
return true;
}
#OnTouch(R.id.fragment_brightness_control_dot_5)
public boolean onDot5Touched() {
setRating(5);
mListener.onRatingChanged(this, 5, true);
return true;
}
#OnTouch(R.id.fragment_brightness_control_dot_6)
public boolean onDot6Touched() {
setRating(6);
mListener.onRatingChanged(this, 6, true);
return true;
}
#OnTouch(R.id.fragment_brightness_control_dot_7)
public boolean onDot7Touched() {
setRating(7);
mListener.onRatingChanged(this, 7, true);
return true;
}
#OnTouch(R.id.fragment_brightness_control_dot_8)
public boolean onDot8Touched() {
setRating(8);
mListener.onRatingChanged(this, 8, true);
return true;
}
#OnTouch(R.id.fragment_brightness_control_dot_9)
public boolean onDot9Touched() {
setRating(9);
mListener.onRatingChanged(this, 9, true);
return true;
}
#OnTouch(R.id.fragment_brightness_control_dot_10)
public boolean onDot10Touched() {
setRating(10);
mListener.onRatingChanged(this, 10, true);
return true;
}
//endregion
public interface OnRatingBarChangeListener {
public void onRatingChanged(DotContainerView ratingBar, float value, boolean fromUser);
}
}
This code works fine, if I tap in a dot all the previous dots'll get selected. The only issue with this is that if I drag my finger across the dots, they don't react as in a RatingBar, only if I tap in each dot. Any idea of how solve this?. And please avoid telling me "Use the RatingBar".
I ended up doing something like this:
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.widget.LinearLayout;
import butterknife.ButterKnife;
import butterknife.InjectView;
/**
* #author astinx
* #since 0.2
* <p>
* Simple widget that shows an array of dots which can be tapped like a {#link android.widget.RatingBar}
*/
public class DotContainerView extends LinearLayout implements View.OnTouchListener {
#InjectView(R.id.view_dot_container)
LinearLayout vDotContainer;
private OnRatingBarChangeListener mListener;
public DotContainerView(Context context) {
super(context);
initialize();
}
public DotContainerView(Context context, AttributeSet attrs) {
super(context, attrs);
initialize();
}
public DotContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initialize();
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public DotContainerView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
initialize();
}
private void initialize() {
View root = LayoutInflater.from(getContext()).inflate(R.layout.view_dot_container, this);
ButterKnife.inject(this, root);
vDotContainer.setOnTouchListener(this);
}
public void setRating(int rating) {
//If the rating = 5
for (int index = 0; index < rating; index++) {
//This sets the children 10, 9, 8, 5...
vDotContainer.getChildAt(Math.abs(index - 10) - 1).setSelected(true);
}
for (int index = rating; index < 10; index++) {
//Ant this sets the children 6, 7, 8, 9, 10
vDotContainer.getChildAt(Math.abs(index - 10) - 1).setSelected(false);
}
}
public void setOnRatingBarChangeListener(DotContainerView.OnRatingBarChangeListener listener) {
mListener = listener;
}
#Override
public boolean onTouch(View v, MotionEvent event) {
float rawX = event.getX();
float rawY = event.getY();
setRating(rawX, rawY);
return true;
}
#Override
public boolean onInterceptHoverEvent(MotionEvent event) {
float rawX = event.getX();
float rawY = event.getY();
setRating(rawX, rawY);
return super.onInterceptHoverEvent(event);
}
#Override
public boolean onInterceptTouchEvent(MotionEvent event) {
float rawX = event.getX();
float rawY = event.getY();
setRating(rawX, rawY);
return false;
}
protected void setRating(float rawX, float rawY) {
Log.d("DotContainer", "x=" + rawX + ";y=" + rawY);
int dotIndexByCoords = 10 - findDotIndexByCoords(rawX, rawY);
setRating(dotIndexByCoords);
mListener.onRatingChanged(this, dotIndexByCoords, true);
}
private View findViewByIndex(int childIndex) {
return vDotContainer.getChildAt(childIndex);
}
/**
* Iterates all over the {#link LinearLayout} searching for the closest child to x,y
* #param x The x axis
* #param y The y axis
* #return The index of the child, -1 if isn't found.
*/
private int findDotIndexByCoords(float x, float y) {
for (int childIndex = 0; childIndex < vDotContainer.getChildCount(); childIndex++) {
float y1 = vDotContainer.getChildAt(childIndex).getY();
float y2 = vDotContainer.getChildAt(childIndex + 1).getY();
if (y1 <= y && y <= y2) {
Log.d("DotContainer", "Child no "+ childIndex);
return childIndex;
}
}
return -1;
}
public interface OnRatingBarChangeListener {
public void onRatingChanged(DotContainerView ratingBar, float value, boolean fromUser);
}
}
I know that're things that can be improved, but for those who want to do something quick withouth yelling and cursing at the RatingBar this is something quick, that gets the job done. Basically is just a LinearLayout that contains an array of ImageView each one has a drawable selector that changes the drawable whether is pressed or not. This class overrides the methodn onInterceptTouchEvent and returns false so it's continiously called, inside this method we check which dot was clicked.
I have an ImageView in a FrameLayout, I want to setup LongClickListener but its failing to work, I tried setting up OnTouchListener and its working flawless, I do not have the slightest idea as to why its not working but below is my code code:
public class DragImageView extends FrameLayout implements View.OnLongClickListener {
ImageView ivDrag;
public DragImageView(Context context) {
super(context);
}
public DragImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public DragImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#TargetApi(Build.VERSION_CODES.HONEYCOMB)
public void AddImageView(View draggableObject, int x, int y, int width, int height) {
LayoutParams lpDraggableView = new LayoutParams(width, height);
lpDraggableView.gravity = Gravity.TOP;
lpDraggableView.leftMargin = x;
lpDraggableView.topMargin = y;
if(draggableObject instanceof ImageView) {
this.ivDrag = (ImageView) draggableObject;
ivDrag.setLayoutParams(lpDraggableView);
ivDrag.setClickable(true);
ivDrag.setLongClickable(true);
ivDrag.setOnLongClickListener(this);
this.addView(ivDrag);
}
}
/**
* Draggable object ontouch listener
* Handle the movement of the object when dragged and dropped
*/
private View.OnTouchListener OnTouchToDrag =new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
FrameLayout.LayoutParams dragParam = (LayoutParams) v.getLayoutParams();
switch(event.getAction())
{
case MotionEvent.ACTION_MOVE:
{
dragParam.topMargin = (int)event.getRawY() - (v.getHeight());
dragParam.leftMargin = (int)event.getRawX() - (v.getWidth()/2);
v.setLayoutParams(dragParam);
break;
}
case MotionEvent.ACTION_UP:
{
dragParam.height = v.getHeight();
dragParam.width = v.getWidth();
dragParam.topMargin = (int)event.getRawY() - (v.getHeight());
dragParam.leftMargin = (int)event.getRawX() - (v.getWidth()/2);
v.setLayoutParams(dragParam);
break;
}
case MotionEvent.ACTION_DOWN:
{
dragParam.height = v.getHeight();//fixed on drag and drop
dragParam.width = v.getWidth();
v.setLayoutParams(dragParam);
break;
}
}
return true;
}
};
#Override
public boolean onLongClick(View view) {
ivDrag.setOnTouchListener(OnTouchToDrag);
Toast.makeText(view.getContext(), "OnLongClick", Toast.LENGTH_SHORT).show();
return false;
}
}
I want to setup LongClickListener but its failing to work
You are not receiving the callbacks from OnLongClickListener because it has no set listener. Since your class implements View.OnLongClickListener and you want to receive the callback in your overridden onLongClick() method, add this class itself as the listener and it will work. I've done so in the constructor (choose the appropriate constructor out of the three as per your initialization of the view):
public DragImageView(Context context, AttributeSet attrs) {
super(context, attrs);
this.setOnLongClickListener(this); // <-- set this class instance as the listener
}
Although I'm surprised how you got it working with OnTouchListener. You probably explicitly added the listener, right?
I have added a seekbar to one of my activities.
Its max value is 5. Now, I want to display the divider values (with increment 1, like 0, 1, 2, 3, 4 and 5) below my seekbar. How can I do that?
Is there any system method to achieve this which I am not able to put my hands on? Any inputs are welcomed.
NOTE : I want to apply any changes programatically, not from xml. The numbers should be separated at equal intervals. I could not edit it that precisely though.
I am supposing you want to display view like below in picture.
if that is the case you have to create your own customSeekbar like give code.
CustomSeekBar.java
public class CustomSeekBar extends SeekBar {
private Paint textPaint;
private Rect textBounds = new Rect();
private String text = "";
public CustomSeekBar(Context context) {
super(context);
textPaint = new Paint();
textPaint.setColor(Color.WHITE);
}
public CustomSeekBar(Context context, AttributeSet attrs) {
super(context, attrs);
textPaint = new Paint();
textPaint.setTypeface(Typeface.SANS_SERIF);
textPaint.setColor(Color.BLACK);
}
public CustomSeekBar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
textPaint = new Paint();
textPaint.setColor(Color.BLACK);
}
#Override
protected synchronized void onDraw(Canvas canvas) {
// First draw the regular progress bar, then custom draw our text
super.onDraw(canvas);
int progress = getProgress();
text = progress + "";
// Now get size of seek bar.
float width = getWidth();
float height = getHeight();
// Set text size.
textPaint.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.BOLD));
textPaint.setTextSize(40);
// Get size of text.
textPaint.getTextBounds(text, 0, text.length(), textBounds);
// Calculate where to start printing text.
float position = (width / getMax()) * getProgress();
// Get start and end points of where text will be printed.
float textXStart = position - textBounds.centerX();
float textXEnd = position + textBounds.centerX();
// Check does not start drawing outside seek bar.
if (textXStart <= 1) textXStart = 20;
if (textXEnd > width) {
textXStart -= (textXEnd - width + 30);
}
// Calculate y text print position.
float yPosition = height;
canvas.drawText(text, textXStart, yPosition, textPaint);
}
public synchronized void setTextColor(int color) {
super.drawableStateChanged();
textPaint.setColor(color);
drawableStateChanged();
}
}
In your Xml file use your custom file like below
<com.waleedsarwar.customseekbar.CustomSeekBar
android:id="#+id/seekbar"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:max="5"
android:paddingBottom="16dp" />
This is another approach. I am extending a linearlayout. I put seekbar and another linearlayout(layout_5) which contains 6 textviews with 0-1-2-3-4-5. Better option would be creating a dynamic image(get width from seekBar) which has these numbers according to segment count.
I force seekbar's indicator to stop at specific points(6 points in your case). Instead of doing this, it is possible to set seekBar's maximum progress value to 5. It will work, but it will not give a good user experience.
public class SegmentedSeekBar extends LinearLayout {
private int[] preDefinedValues;
private int currentProgressIndex;
private SeekBar seekBar;
private int segmentCount = 5:
public SegmentedSeekBar(Context context) {
this(context, null, 0);
}
public SegmentedSeekBar(Context context, AttributeSet attrs) {
this(context, attrs, android.R.attr.seekBarStyle);
}
public SegmentedSeekBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray a = context.getTheme().obtainStyledAttributes(
attrs,
R.styleable.SegmentedSeekBar,
0, 0);
try {
segmentCount =
a.getInt(R.styleable.SegmentedSeekBar_segmentCount, -1);
} finally {
a.recycle();
}
init();
}
public void init() {
//this values will be used when you need to set progress
preDefinedValues = new int[segmentCount];
for(int i = 0; i < preDefinedValues.length; i++) {
preDefinedValues[i] = (100/(segmentCount-1)) * i;
}
//Get layout_5
//which is linearlayout with 6 textviews
LayoutInflater inflater = (LayoutInflater) getContext()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View sliderView = inflater.inflate(
getSliderId(segmentCount), null);
//seekbar already inside the linearlayout
seekBar = (SeekBar)sliderView.findViewById(R.id.seek_bar);
//linear layout is vertically align
//so add your 6 textview linearlayout
addView(sliderView);
seekBar.setOnTouchListener(seekBarTouchListener);
}
private int getSliderId(int size) {
return R.layout.layout_5;
}
//this method sets progress which is seen in UI not actual progress
//It uses the conversion that we did in beginning
public synchronized void setProgress(int progress) {
if(preDefinedValues != null && progress < preDefinedValues.length && progress >= 0) {
seekBar.setProgress(preDefinedValues[progress]);
currentProgressIndex = progress;
}
}
//this listener make sure the right progress is seen in ui
//take action when user finish with changing progress
SeekBar.OnSeekBarChangeListener onSeekBarChangeListener = new SeekBar.OnSeekBarChangeListener() {
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
int index = 0;
for(int i = 0; i < preDefinedValues.length; i++) {
//try to find closest preDefinedvalues by comparing with latest value
if(Math.abs(seekBar.getProgress() - preDefinedValues[i]) < Math.abs(seekBar.getProgress() - preDefinedValues[index])) {
index = i;
}
}
setProgress(index);
}
};
}
i just read this How can I add an image on EditText and want to know if it is possible to add action on the icon when clicked...
yes it's possible just create CustomEediTtext extends from EditText. check this solution
Create a customized EditText class CustomEditText.java:
public class CustomEditText extends EditText {
private Drawable dRight;
private Rect rBounds;
public CustomEditText(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public CustomEditText(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomEditText(Context context) {
super(context);
}
#Override
public void setCompoundDrawables(Drawable left, Drawable top,
Drawable right, Drawable bottom) {
if(right !=null)
{
dRight = right;
}
super.setCompoundDrawables(left, top, right, bottom);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_UP && dRight!=null) {
rBounds = dRight.getBounds();
final int x = (int)event.getX();
final int y = (int)event.getY();
if(x>=(this.getRight()-rBounds.width()) && x<=(this.getRight()-
this.getPaddingRight()) && y>=this.getPaddingTop() && y<=(this.getHeight()-
this.getPaddingBottom())) {
//System.out.println("touch");
this.setText("");
event.setAction(MotionEvent.ACTION_CANCEL);//use this to prevent the keyboard
}
}
return super.onTouchEvent(event);
}
#Override
protected void finalize() throws Throwable {
dRight = null;
rBounds = null;
super.finalize();
}
}