mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-26 20:33:53 +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.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.view.ViewCompat;
|
||||
import android.support.v7.widget.AppCompatSpinner;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.text.TextUtils;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.AdapterView.OnItemSelectedListener;
|
||||
import android.widget.EditText;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.OnTouch;
|
||||
import butterknife.Optional;
|
||||
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.BiMap;
|
||||
import fr.free.nrw.commons.utils.ViewUtil;
|
||||
|
|
@ -31,8 +27,6 @@ import io.reactivex.subjects.BehaviorSubject;
|
|||
import io.reactivex.subjects.Subject;
|
||||
import timber.log.Timber;
|
||||
|
||||
import static android.view.MotionEvent.ACTION_UP;
|
||||
|
||||
class DescriptionsAdapter extends RecyclerView.Adapter<DescriptionsAdapter.ViewHolder> {
|
||||
|
||||
private Title title;
|
||||
|
|
@ -119,7 +113,7 @@ class DescriptionsAdapter extends RecyclerView.Adapter<DescriptionsAdapter.ViewH
|
|||
AppCompatSpinner spinnerDescriptionLanguages;
|
||||
|
||||
@BindView(R.id.description_item_edit_text)
|
||||
EditText descItemEditText;
|
||||
CustomEditText descItemEditText;
|
||||
|
||||
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 {
|
||||
Description description = descriptions.get(position - 1);
|
||||
Timber.d("Description is " + description);
|
||||
|
|
@ -168,9 +174,7 @@ class DescriptionsAdapter extends RecyclerView.Adapter<DescriptionsAdapter.ViewH
|
|||
descItemEditText.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null);
|
||||
}
|
||||
|
||||
descItemEditText.addTextChangedListener(new AbstractTextWatcher(descriptionText -> {
|
||||
description.setDescriptionText(descriptionText);
|
||||
}));
|
||||
descItemEditText.addTextChangedListener(new AbstractTextWatcher(description::setDescriptionText));
|
||||
descItemEditText.setOnFocusChangeListener((v, hasFocus) -> {
|
||||
if (!hasFocus) {
|
||||
ViewUtil.hideKeyboard(v);
|
||||
|
|
@ -179,95 +183,75 @@ class DescriptionsAdapter extends RecyclerView.Adapter<DescriptionsAdapter.ViewH
|
|||
}
|
||||
});
|
||||
|
||||
SpinnerLanguagesAdapter languagesAdapter = new SpinnerLanguagesAdapter(context,
|
||||
R.layout.row_item_languages_spinner, selectedLanguages);
|
||||
languagesAdapter.notifyDataSetChanged();
|
||||
spinnerDescriptionLanguages.setAdapter(languagesAdapter);
|
||||
|
||||
if (description.getSelectedLanguageIndex() == -1) {
|
||||
if (position == 1) {
|
||||
int defaultLocaleIndex = languagesAdapter.getIndexOfUserDefaultLocale(context);
|
||||
spinnerDescriptionLanguages.setSelection(defaultLocaleIndex);
|
||||
} else {
|
||||
spinnerDescriptionLanguages.setSelection(0);
|
||||
}
|
||||
} else {
|
||||
spinnerDescriptionLanguages.setSelection(description.getSelectedLanguageIndex());
|
||||
selectedLanguages.put(spinnerDescriptionLanguages, description.getLanguageCode());
|
||||
}
|
||||
|
||||
//TODO do it the butterknife way
|
||||
spinnerDescriptionLanguages.setOnItemSelectedListener(new OnItemSelectedListener() {
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> adapterView, View view, int position,
|
||||
long l) {
|
||||
description.setSelectedLanguageIndex(position);
|
||||
String languageCode = ((SpinnerLanguagesAdapter) adapterView.getAdapter()).getLanguageCode(position);
|
||||
description.setLanguageCode(languageCode);
|
||||
selectedLanguages.remove(adapterView);
|
||||
selectedLanguages.put(adapterView, languageCode);
|
||||
((SpinnerLanguagesAdapter) adapterView.getAdapter()).selectedLangCode = languageCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNothingSelected(AdapterView<?> adapterView) {
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Optional
|
||||
@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;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 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,
|
||||
R.layout.row_item_languages_spinner, selectedLanguages);
|
||||
languagesAdapter.notifyDataSetChanged();
|
||||
spinnerDescriptionLanguages.setAdapter(languagesAdapter);
|
||||
|
||||
private Drawable getInfoIcon() {
|
||||
return context.getResources()
|
||||
.getDrawable(R.drawable.mapbox_info_icon_default);
|
||||
if (description.getSelectedLanguageIndex() == -1) {
|
||||
if (position == 1) {
|
||||
int defaultLocaleIndex = languagesAdapter.getIndexOfUserDefaultLocale(context);
|
||||
spinnerDescriptionLanguages.setSelection(defaultLocaleIndex);
|
||||
} else {
|
||||
spinnerDescriptionLanguages.setSelection(0);
|
||||
}
|
||||
} else {
|
||||
spinnerDescriptionLanguages.setSelection(description.getSelectedLanguageIndex());
|
||||
selectedLanguages.put(spinnerDescriptionLanguages, description.getLanguageCode());
|
||||
}
|
||||
|
||||
//TODO do it the butterknife way
|
||||
spinnerDescriptionLanguages.setOnItemSelectedListener(new OnItemSelectedListener() {
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> adapterView, View view, int position,
|
||||
long l) {
|
||||
description.setSelectedLanguageIndex(position);
|
||||
String languageCode = ((SpinnerLanguagesAdapter) adapterView.getAdapter()).getLanguageCode(position);
|
||||
description.setLanguageCode(languageCode);
|
||||
selectedLanguages.remove(adapterView);
|
||||
selectedLanguages.put(adapterView, languageCode);
|
||||
((SpinnerLanguagesAdapter) adapterView.getAdapter()).selectedLangCode = languageCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNothingSelected(AdapterView<?> adapterView) {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracted out the method to get the icon drawable
|
||||
* @return
|
||||
*/
|
||||
private Drawable getInfoIcon() {
|
||||
return context.getResources().getDrawable(R.drawable.mapbox_info_icon_default);
|
||||
}
|
||||
}
|
||||
|
||||
public interface Callback {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
|
|
@ -20,14 +19,16 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:layout_weight="8">
|
||||
|
||||
<EditText
|
||||
<fr.free.nrw.commons.ui.widget.CustomEditText
|
||||
android:id="@+id/description_item_edit_text"
|
||||
android:layout_width="match_parent"
|
||||
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:imeOptions="flagNoExtractUi"
|
||||
android:drawablePadding="4dp"
|
||||
android:paddingLeft="4dp"
|
||||
android:paddingRight="4dp"
|
||||
android:drawableRight="@drawable/mapbox_info_icon_default"
|
||||
android:inputType="textMultiLine" />
|
||||
</android.support.design.widget.TextInputLayout>
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
android:layout_height="wrap_content"
|
||||
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:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue