mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-27 04:43:54 +01:00
Fix desc icon clickable area (#2025)
* Fix desc icon clickable area * Added java docs
This commit is contained in:
parent
d62a7586e0
commit
a248e6114c
4 changed files with 286 additions and 97 deletions
|
|
@ -0,0 +1,204 @@
|
||||||
|
package fr.free.nrw.commons.ui.widget;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.Rect;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.view.MotionEvent;
|
||||||
|
import android.widget.EditText;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom edit text with a drawable click listener
|
||||||
|
* https://stackoverflow.com/questions/13135447/setting-onclicklistener-for-the-drawable-right-of-an-edittext
|
||||||
|
*/
|
||||||
|
@SuppressLint("AppCompatCustomView")
|
||||||
|
public class CustomEditText extends EditText {
|
||||||
|
|
||||||
|
private Drawable drawableRight;
|
||||||
|
private Drawable drawableLeft;
|
||||||
|
private Drawable drawableTop;
|
||||||
|
private Drawable drawableBottom;
|
||||||
|
|
||||||
|
int actionX, actionY;
|
||||||
|
|
||||||
|
private DrawableClickListener clickListener;
|
||||||
|
|
||||||
|
public CustomEditText(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
// this Contructure required when you are using this view in xml
|
||||||
|
}
|
||||||
|
|
||||||
|
public CustomEditText(Context context, AttributeSet attrs, int defStyle) {
|
||||||
|
super(context, attrs, defStyle);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void onDraw(Canvas canvas) {
|
||||||
|
super.onDraw(canvas);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
||||||
|
super.onSizeChanged(w, h, oldw, oldh);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setCompoundDrawables(Drawable left, Drawable top,
|
||||||
|
Drawable right, Drawable bottom) {
|
||||||
|
if (left != null) {
|
||||||
|
drawableLeft = left;
|
||||||
|
}
|
||||||
|
if (right != null) {
|
||||||
|
drawableRight = right;
|
||||||
|
}
|
||||||
|
if (top != null) {
|
||||||
|
drawableTop = top;
|
||||||
|
}
|
||||||
|
if (bottom != null) {
|
||||||
|
drawableBottom = bottom;
|
||||||
|
}
|
||||||
|
super.setCompoundDrawables(left, top, right, bottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fires the appropriate drawable click listener on touching the icon
|
||||||
|
* @param event
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean onTouchEvent(MotionEvent event) {
|
||||||
|
Rect bounds;
|
||||||
|
if (event.getAction() == MotionEvent.ACTION_DOWN) {
|
||||||
|
actionX = (int) event.getX();
|
||||||
|
actionY = (int) event.getY();
|
||||||
|
if (drawableBottom != null
|
||||||
|
&& drawableBottom.getBounds().contains(actionX, actionY)) {
|
||||||
|
clickListener.onClick(DrawableClickListener.DrawablePosition.BOTTOM);
|
||||||
|
return super.onTouchEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (drawableTop != null
|
||||||
|
&& drawableTop.getBounds().contains(actionX, actionY)) {
|
||||||
|
clickListener.onClick(DrawableClickListener.DrawablePosition.TOP);
|
||||||
|
return super.onTouchEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
// this works for left since container shares 0,0 origin with bounds
|
||||||
|
if (drawableLeft != null) {
|
||||||
|
bounds = null;
|
||||||
|
bounds = drawableLeft.getBounds();
|
||||||
|
|
||||||
|
int x, y;
|
||||||
|
int extraTapArea = (int) (13 * getResources().getDisplayMetrics().density + 0.5);
|
||||||
|
|
||||||
|
x = actionX;
|
||||||
|
y = actionY;
|
||||||
|
|
||||||
|
if (!bounds.contains(actionX, actionY)) {
|
||||||
|
/** Gives the +20 area for tapping. */
|
||||||
|
x = (int) (actionX - extraTapArea);
|
||||||
|
y = (int) (actionY - extraTapArea);
|
||||||
|
|
||||||
|
if (x <= 0)
|
||||||
|
x = actionX;
|
||||||
|
if (y <= 0)
|
||||||
|
y = actionY;
|
||||||
|
|
||||||
|
/** Creates square from the smallest value */
|
||||||
|
if (x < y) {
|
||||||
|
y = x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bounds.contains(x, y) && clickListener != null) {
|
||||||
|
clickListener
|
||||||
|
.onClick(DrawableClickListener.DrawablePosition.LEFT);
|
||||||
|
event.setAction(MotionEvent.ACTION_CANCEL);
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (drawableRight != null) {
|
||||||
|
|
||||||
|
bounds = null;
|
||||||
|
bounds = drawableRight.getBounds();
|
||||||
|
|
||||||
|
int x, y;
|
||||||
|
int extraTapArea = 13;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IF USER CLICKS JUST OUT SIDE THE RECTANGLE OF THE DRAWABLE
|
||||||
|
* THAN ADD X AND SUBTRACT THE Y WITH SOME VALUE SO THAT AFTER
|
||||||
|
* CALCULATING X AND Y CO-ORDINATE LIES INTO THE DRAWBABLE
|
||||||
|
* BOUND. - this process help to increase the tappable area of
|
||||||
|
* the rectangle.
|
||||||
|
*/
|
||||||
|
x = (int) (actionX + extraTapArea);
|
||||||
|
y = (int) (actionY - extraTapArea);
|
||||||
|
|
||||||
|
/**Since this is right drawable subtract the value of x from the width
|
||||||
|
* of view. so that width - tappedarea will result in x co-ordinate in drawable bound.
|
||||||
|
*/
|
||||||
|
x = getWidth() - x;
|
||||||
|
|
||||||
|
/*x can be negative if user taps at x co-ordinate just near the width.
|
||||||
|
* e.g views width = 300 and user taps 290. Then as per previous calculation
|
||||||
|
* 290 + 13 = 303. So subtract X from getWidth() will result in negative value.
|
||||||
|
* So to avoid this add the value previous added when x goes negative.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (x <= 0) {
|
||||||
|
x += extraTapArea;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If result after calculating for extra tappable area is negative.
|
||||||
|
* assign the original value so that after subtracting
|
||||||
|
* extratapping area value doesn't go into negative value.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (y <= 0)
|
||||||
|
y = actionY;
|
||||||
|
|
||||||
|
/**If drawble bounds contains the x and y points then move ahead.*/
|
||||||
|
if (bounds.contains(x, y) && clickListener != null) {
|
||||||
|
clickListener
|
||||||
|
.onClick(DrawableClickListener.DrawablePosition.RIGHT);
|
||||||
|
event.setAction(MotionEvent.ACTION_CANCEL);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return super.onTouchEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return super.onTouchEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void finalize() throws Throwable {
|
||||||
|
drawableRight = null;
|
||||||
|
drawableBottom = null;
|
||||||
|
drawableLeft = null;
|
||||||
|
drawableTop = null;
|
||||||
|
super.finalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attaches the drawable click listener to the custom edit text
|
||||||
|
* @param listener
|
||||||
|
*/
|
||||||
|
public void setDrawableClickListener(DrawableClickListener listener) {
|
||||||
|
this.clickListener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for drawable click listener
|
||||||
|
*/
|
||||||
|
public interface DrawableClickListener {
|
||||||
|
enum DrawablePosition {TOP, BOTTOM, LEFT, RIGHT}
|
||||||
|
void onClick(DrawablePosition target);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -4,26 +4,22 @@ import android.content.Context;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.support.v4.view.ViewCompat;
|
|
||||||
import android.support.v7.widget.AppCompatSpinner;
|
import android.support.v7.widget.AppCompatSpinner;
|
||||||
import android.support.v7.widget.RecyclerView;
|
import android.support.v7.widget.RecyclerView;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.MotionEvent;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.AdapterView;
|
import android.widget.AdapterView;
|
||||||
import android.widget.AdapterView.OnItemSelectedListener;
|
import android.widget.AdapterView.OnItemSelectedListener;
|
||||||
import android.widget.EditText;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import butterknife.BindView;
|
import butterknife.BindView;
|
||||||
import butterknife.ButterKnife;
|
import butterknife.ButterKnife;
|
||||||
import butterknife.OnTouch;
|
|
||||||
import butterknife.Optional;
|
|
||||||
import fr.free.nrw.commons.R;
|
import fr.free.nrw.commons.R;
|
||||||
|
import fr.free.nrw.commons.ui.widget.CustomEditText;
|
||||||
import fr.free.nrw.commons.utils.AbstractTextWatcher;
|
import fr.free.nrw.commons.utils.AbstractTextWatcher;
|
||||||
import fr.free.nrw.commons.utils.BiMap;
|
import fr.free.nrw.commons.utils.BiMap;
|
||||||
import fr.free.nrw.commons.utils.ViewUtil;
|
import fr.free.nrw.commons.utils.ViewUtil;
|
||||||
|
|
@ -31,8 +27,6 @@ import io.reactivex.subjects.BehaviorSubject;
|
||||||
import io.reactivex.subjects.Subject;
|
import io.reactivex.subjects.Subject;
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
import static android.view.MotionEvent.ACTION_UP;
|
|
||||||
|
|
||||||
class DescriptionsAdapter extends RecyclerView.Adapter<DescriptionsAdapter.ViewHolder> {
|
class DescriptionsAdapter extends RecyclerView.Adapter<DescriptionsAdapter.ViewHolder> {
|
||||||
|
|
||||||
private Title title;
|
private Title title;
|
||||||
|
|
@ -119,7 +113,7 @@ class DescriptionsAdapter extends RecyclerView.Adapter<DescriptionsAdapter.ViewH
|
||||||
AppCompatSpinner spinnerDescriptionLanguages;
|
AppCompatSpinner spinnerDescriptionLanguages;
|
||||||
|
|
||||||
@BindView(R.id.description_item_edit_text)
|
@BindView(R.id.description_item_edit_text)
|
||||||
EditText descItemEditText;
|
CustomEditText descItemEditText;
|
||||||
|
|
||||||
private View view;
|
private View view;
|
||||||
|
|
||||||
|
|
@ -154,6 +148,18 @@ class DescriptionsAdapter extends RecyclerView.Adapter<DescriptionsAdapter.ViewH
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
descItemEditText.setDrawableClickListener(target -> {
|
||||||
|
switch (target) {
|
||||||
|
case RIGHT:
|
||||||
|
if (getAdapterPosition() == 0) {
|
||||||
|
callback.showAlert(R.string.media_detail_title, R.string.title_info);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
Description description = descriptions.get(position - 1);
|
Description description = descriptions.get(position - 1);
|
||||||
Timber.d("Description is " + description);
|
Timber.d("Description is " + description);
|
||||||
|
|
@ -168,9 +174,7 @@ class DescriptionsAdapter extends RecyclerView.Adapter<DescriptionsAdapter.ViewH
|
||||||
descItemEditText.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null);
|
descItemEditText.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
descItemEditText.addTextChangedListener(new AbstractTextWatcher(descriptionText -> {
|
descItemEditText.addTextChangedListener(new AbstractTextWatcher(description::setDescriptionText));
|
||||||
description.setDescriptionText(descriptionText);
|
|
||||||
}));
|
|
||||||
descItemEditText.setOnFocusChangeListener((v, hasFocus) -> {
|
descItemEditText.setOnFocusChangeListener((v, hasFocus) -> {
|
||||||
if (!hasFocus) {
|
if (!hasFocus) {
|
||||||
ViewUtil.hideKeyboard(v);
|
ViewUtil.hideKeyboard(v);
|
||||||
|
|
@ -179,6 +183,31 @@ class DescriptionsAdapter extends RecyclerView.Adapter<DescriptionsAdapter.ViewH
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
descItemEditText.setDrawableClickListener(target -> {
|
||||||
|
switch (target) {
|
||||||
|
case RIGHT:
|
||||||
|
if (getAdapterPosition() == 1) {
|
||||||
|
callback.showAlert(R.string.media_detail_description,
|
||||||
|
R.string.description_info);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
initLanguageSpinner(position, description);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracted out the function to init the language spinner with different system supported languages
|
||||||
|
* @param position
|
||||||
|
* @param description
|
||||||
|
*/
|
||||||
|
private void initLanguageSpinner(int position, Description description) {
|
||||||
SpinnerLanguagesAdapter languagesAdapter = new SpinnerLanguagesAdapter(context,
|
SpinnerLanguagesAdapter languagesAdapter = new SpinnerLanguagesAdapter(context,
|
||||||
R.layout.row_item_languages_spinner, selectedLanguages);
|
R.layout.row_item_languages_spinner, selectedLanguages);
|
||||||
languagesAdapter.notifyDataSetChanged();
|
languagesAdapter.notifyDataSetChanged();
|
||||||
|
|
@ -216,58 +245,13 @@ class DescriptionsAdapter extends RecyclerView.Adapter<DescriptionsAdapter.ViewH
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
/**
|
||||||
|
* Extracted out the method to get the icon drawable
|
||||||
@Optional
|
* @return
|
||||||
@OnTouch(R.id.description_item_edit_text)
|
*/
|
||||||
boolean descriptionInfo(View view, MotionEvent motionEvent) {
|
|
||||||
//Title info is visible only for the title
|
|
||||||
if (getAdapterPosition() == 0) {
|
|
||||||
if (ViewCompat.getLayoutDirection(view) == ViewCompat.LAYOUT_DIRECTION_LTR) {
|
|
||||||
final int value = view.getRight() - descItemEditText
|
|
||||||
.getCompoundDrawables()[2]
|
|
||||||
.getBounds().width();
|
|
||||||
if (motionEvent.getAction() == ACTION_UP && motionEvent.getRawX() >= value) {
|
|
||||||
callback.showAlert(R.string.media_detail_title, R.string.title_info);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
final int value = descItemEditText.getLeft() + descItemEditText
|
|
||||||
.getCompoundDrawables()[0]
|
|
||||||
.getBounds().width();
|
|
||||||
if (motionEvent.getAction() == ACTION_UP && motionEvent.getRawX() <= value) {
|
|
||||||
callback.showAlert(R.string.media_detail_title, R.string.title_info);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//Description info is visible only for the first description
|
|
||||||
} else if (getAdapterPosition() == 1) {
|
|
||||||
final int value;
|
|
||||||
if (ViewCompat.getLayoutDirection(view) == ViewCompat.LAYOUT_DIRECTION_LTR) {
|
|
||||||
value = view.getRight() - descItemEditText.getCompoundDrawables()[2].getBounds().width();
|
|
||||||
if (motionEvent.getAction() == ACTION_UP && motionEvent.getRawX() >= value) {
|
|
||||||
callback.showAlert(R.string.media_detail_description,
|
|
||||||
R.string.description_info);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
value = descItemEditText.getLeft() + descItemEditText
|
|
||||||
.getCompoundDrawables()[0]
|
|
||||||
.getBounds().width();
|
|
||||||
if (motionEvent.getAction() == ACTION_UP && motionEvent.getRawX() <= value) {
|
|
||||||
callback.showAlert(R.string.media_detail_description,
|
|
||||||
R.string.description_info);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Drawable getInfoIcon() {
|
private Drawable getInfoIcon() {
|
||||||
return context.getResources()
|
return context.getResources().getDrawable(R.drawable.mapbox_info_icon_default);
|
||||||
.getDrawable(R.drawable.mapbox_info_icon_default);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface Callback {
|
public interface Callback {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
|
|
@ -20,14 +19,16 @@
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="8">
|
android:layout_weight="8">
|
||||||
|
|
||||||
<EditText
|
<fr.free.nrw.commons.ui.widget.CustomEditText
|
||||||
android:id="@+id/description_item_edit_text"
|
android:id="@+id/description_item_edit_text"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:drawableEnd="@drawable/mapbox_info_icon_default"
|
|
||||||
android:drawableRight="@drawable/mapbox_info_icon_default"
|
|
||||||
android:hint="@string/share_description_hint"
|
android:hint="@string/share_description_hint"
|
||||||
android:imeOptions="flagNoExtractUi"
|
android:imeOptions="flagNoExtractUi"
|
||||||
|
android:drawablePadding="4dp"
|
||||||
|
android:paddingLeft="4dp"
|
||||||
|
android:paddingRight="4dp"
|
||||||
|
android:drawableRight="@drawable/mapbox_info_icon_default"
|
||||||
android:inputType="textMultiLine" />
|
android:inputType="textMultiLine" />
|
||||||
</android.support.design.widget.TextInputLayout>
|
</android.support.design.widget.TextInputLayout>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
tools:showIn="@layout/activity_upload">
|
tools:showIn="@layout/activity_upload">
|
||||||
|
|
||||||
<android.support.design.widget.TextInputEditText
|
<fr.free.nrw.commons.ui.widget.CustomEditText
|
||||||
android:id="@+id/description_item_edit_text"
|
android:id="@+id/description_item_edit_text"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue