Android: Remove only bottom FadingEdge effect from scroll bar - android

I know how to disable fadingedge from scrollbar but what I need is to disable just the bottom fading edge without disabling the top fading edge effect, is that possible?

You can achieve the effect you want by extending the ScrollView and overriding one of those two methods:
float getTopFadingEdgeStrength()
float getBottomFadingEdgeStrength()
They'll alow you to change the size of the fading edge - just set bottom value to 0 and you are ready to go :)
Code example with bottom fading turned off:
/**
* Created by scana on 14.12.14.
*/
public class TopFadeEdgeScrollView extends ScrollView {
public TopFadeEdgeScrollView(Context context) {
super(context);
}
public TopFadeEdgeScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public TopFadeEdgeScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
protected float getBottomFadingEdgeStrength() {
return 0.0f;
}
}

scana's answer is correct.
Here's a Kotlin version of his answer that has methods to disable specific edges.
import android.content.Context
import android.util.AttributeSet
import android.widget.ScrollView
class FadingEdgeScrollView #JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyle: Int = 0
) : ScrollView(context, attrs, defStyle) {
var topFadingStrength: Float? = null
var bottomFadingStrength: Float? = null
var leftFadingStrength: Float? = null
var rightFadingStrength: Float? = null
override fun getTopFadingEdgeStrength(): Float {
return topFadingStrength ?: super.getTopFadingEdgeStrength()
}
override fun getBottomFadingEdgeStrength(): Float {
return bottomFadingStrength ?: super.getBottomFadingEdgeStrength()
}
override fun getLeftFadingEdgeStrength(): Float {
return leftFadingStrength ?: super.getLeftFadingEdgeStrength()
}
override fun getRightFadingEdgeStrength(): Float {
return rightFadingStrength ?: super.getRightFadingEdgeStrength()
}
fun disableTopFade() {
topFadingStrength = 0f
}
fun disableBottomFade() {
bottomFadingStrength = 0f
}
fun disableLeftFade() {
leftFadingStrength = 0f
}
fun disableRightFade() {
rightFadingStrength = 0f
}
}

Related

Call requires API level 29 (current min is 21): `android.widget.NumberPicker#setTextColor`

I want to change selected field of text color using setTextColor. But Android Studio gives me this error. What should I do? Min SDK is 21.
This is code of my CustomNumberPicker class:
import android.annotation.TargetApi
import android.content.Context
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Typeface
import android.graphics.drawable.Drawable
import android.os.Build
import android.os.Build.VERSION
import android.os.Build.VERSION_CODES
import android.util.AttributeSet
import android.util.Log
import android.view.View
import android.view.ViewGroup
import android.widget.EditText
import android.widget.NumberPicker
import android.widget.NumberPicker.OnScrollListener
import androidx.annotation.ColorInt
import androidx.core.content.ContextCompat
import androidx.core.content.res.ResourcesCompat
import androidx.core.graphics.drawable.DrawableCompat
import androidx.core.widget.TextViewCompat
import ir.partsoftware.cup.R
import timber.log.Timber
class CustomNumberPicker : NumberPicker {
constructor(context: Context?) : super(context) {
init()
}
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) {
init()
}
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
init()
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
constructor(
context: Context?,
attrs: AttributeSet?,
defStyleAttr: Int,
defStyleRes: Int
) : super(context, attrs, defStyleAttr, defStyleRes) {
init()
}
private fun init() {
setDividerColor(ContextCompat.getColor(context, R.color.color_secondary))
setNumberPickerTextColor(this, ContextCompat.getColor(context, R.color.color_secondary))
this.setOnValueChangedListener { picker, oldVal, newVal ->
setNumberPickerTextColor(this, ContextCompat.getColor(context, R.color.color_secondary))
}
this.setOnScrollListener { numberPicker, scrollState ->
setNumberPickerTextColor(this, ContextCompat.getColor(context, R.color.color_secondary))
}
}
private fun setNumberPickerTextColor(numberPicker: NumberPicker, color: Int) {
if (VERSION.SDK_INT >= VERSION_CODES.Q) {
numberPicker.textColor = color
} else {
val count = numberPicker.childCount
for (i in 0 until count) {
val child = numberPicker.getChildAt(i)
if (child is EditText) {
try {
child.setTextColor(color)
val fieldSelectorWheelPaint = numberPicker.javaClass.getDeclaredField("mSelectorWheelPaint")
val paint = fieldSelectorWheelPaint[numberPicker] as Paint
paint.color = color
fieldSelectorWheelPaint.isAccessible = true
numberPicker.invalidate()
} catch (ex: java.lang.Exception) {
// Ignore
}
}
}
}
}
private fun setDividerColor(#ColorInt color: Int) {
try {
val fDividerDrawable =
NumberPicker::class.java.getDeclaredField("mSelectionDivider")
fDividerDrawable.isAccessible = true
val d = fDividerDrawable[this] as Drawable
DrawableCompat.setTint(d, color)
d.invalidateSelf()
postInvalidate()
} catch (e: Exception) {
Timber.d(e)
}
}
override fun addView(
child: View,
index: Int,
params: ViewGroup.LayoutParams
) {
super.addView(child, index, params)
updateView(child)
}
private fun updateView(view: View) {
if (view is EditText) {
try {
TextViewCompat.setTextAppearance(view, R.style.TextAppearance_PartPay_NumPicker)
val customFont: Typeface? = ResourcesCompat.getFont(context, R.font.iran_yekan)
view.typeface = customFont
// setNumberPickerTextColor(ContextCompat.getColor(context, R.color.color_secondary))
} catch (e: Exception) {
Timber.d(e)
}
}
}
}
Try the next code. Will use reflection when the API is not accesible:
public void setNumberPickerTextColor(final NumberPicker numberPicker, final int color){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
numberPicker.setTextColor(color);
}
else {
final int count = numberPicker.getChildCount();
for (int i = 0; i < count; i++) {
final View child = numberPicker.getChildAt(i);
if (child instanceof EditText) {
try {
((EditText)child).setTextColor(color);
numberPicker.invalidate();
final Field fieldSelectorWheelPaint = numberPicker.getClass().getDeclaredField("mSelectorWheelPaint");
boolean isAccessible = fieldSelectorWheelPaint.isAccessible();
fieldSelectorWheelPaint.setAccessible(true);
final Paint paint = (Paint)fieldSelectorWheelPaint.get(numberPicker);
if (paint != null){
paint.setColor(color);
fieldSelectorWheelPaint.setAccessible(isAccessible);
numberPicker.invalidate();
}
final Field fieldSelectionDivider = numberPicker.getClass().getDeclaredField("mSelectionDivider");
isAccessible = fieldSelectionDivider.isAccessible();
fieldSelectionDivider.setAccessible(true);
fieldSelectionDivider.set(numberPicker, null);
fieldSelectionDivider.setAccessible(isAccessible);
numberPicker.invalidate();
}
catch (Exception ex) {
// Ignore
}
}
}
}
}
You may call this method the first time you get a reference to the control, and in addition if the color doesn't persist after scrolling, then hook listener such as next:
numberPicker.setOnValueChangedListener(new NumberPicker.OnValueChangeListener() {
#Override
public void onValueChange(final NumberPicker picker, final int oldVal, final int newVal) {
setNumberPickerTextColor(numberPicker, Color.RED);
}
});
Or alternatively you can also hook a scroll listener, although the above setOnValueChangedListener example is more optimal, as it will only perform the update when the value is changed. To improve the next scroll method you could check if the scrollState is in an idle state, so it is only called when scrolling ends:
numberPicker.setOnScrollListener(new NumberPicker.OnScrollListener() {
#Override
public void onScrollStateChange(final NumberPicker numberPicker, final int scrollState) {
setNumberPickerTextColor(numberPicker, Color.RED);
}
});
UPDATE:
The next section is specific only to your updated question code.
The problem is that you are extending a NumberPicker class, in such case you need to use the getDeclaredField on the super class. My above answer can be used only when not extending the NumberPicker class.
In addition you've placed the isAccesible in the wrong line, it needs to be a bit before to make it accesible.
Next is the correction to your code which can be used perfectly when extending a NumberPicker class. You can see that getDeclaredField is preceded by superclass, and isAccessible is at the correct position:
private fun setNumberPickerTextColor(numberPicker: NumberPicker, color: Int) {
if (VERSION.SDK_INT >= VERSION_CODES.Q) {
numberPicker.textColor = color
} else {
val count = numberPicker.childCount
for (i in 0 until count) {
val child = numberPicker.getChildAt(i)
if (child is EditText) {
try {
child.setTextColor(color)
val fieldSelectorWheelPaint = numberPicker.javaClass.superclass.getDeclaredField("mSelectorWheelPaint")
fieldSelectorWheelPaint.isAccessible = true
val paint = fieldSelectorWheelPaint[numberPicker] as Paint
paint.color = color
numberPicker.invalidate()
} catch (ex: java.lang.Exception) {
// Ignore
}
}
}
}
}

Restrict user scrolling in RecyclerView

In my project I use a RecyclerView that I only want to scroll by calling the startSmoothScroll() method of the LayoutManager:
private fun next(){
val layoutManager = pager.layoutManager as BattlePageLayoutManager
layoutManager.startSmoothScroll(smoothScroller(layoutManager.findFirstVisibleItemPosition() + 1))
layoutManager.finishScroll()
}
I do not want the user to be able to scroll manually, e. g. by swiping.
I already tried to achieve this through overriding the method onInterceptTouchEvent() of the parent FrameLayout.
override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
if (ev.actionMasked == MotionEvent.ACTION_DOWN){
startClickTime = System.currentTimeMillis()
startX = ev.x
startY = ev.y
}
val allowEvent = (System.currentTimeMillis() - startClickTime) < 1000 && (startX-ev.x).absoluteValue < 15 && (startY-ev.y).absoluteValue < 15
return !allowEvent
}
That worked basically, but it occured that after double-tapping the View users are able to scroll by themselves.
Do you have any other ideas to approach this?
Did you try overriding canScrollVertically() method in the LayoutManager?
mLayoutManager = new LinearLayoutManager(getActivity()) {
#Override
public boolean canScrollVertically() {
return false;
}
};
Edit:
Create your own implementation of RecyclerView which it disables the touch event while scrolling is performing. Then you have to change the RecyclerView class in the xml file and Fragment/Activity with it.
Find here an example in Kotlin
class MyRecyclerView : RecyclerView {
constructor(context: Context) : super(context) {}
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {}
constructor(context: Context, attrs: AttributeSet?, defStyle: Int) : super(context, attrs, defStyle) {}
override fun onInterceptTouchEvent(e: MotionEvent): Boolean {
return if (scrollState != RecyclerView.SCROLL_STATE_IDLE) false else super.onInterceptTouchEvent(e)
}
}
And in Java
public class MyRecyclerView extends RecyclerView {
public MyRecyclerView(Context context) {
super(context);
}
public MyRecyclerView(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
}
public MyRecyclerView(Context context, #Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
public boolean onInterceptTouchEvent(MotionEvent e) {
if(getScrollState() != SCROLL_STATE_IDLE)
return false;
return super.onInterceptTouchEvent(e);
}
}
You might want to block the user interaction with RecyclerView, not with FrameLayout itself.
Check RecyclerView.OnItemTouchListener.
In your RecyclerView, you can implement OnItemTouchListener and override every method to do nothing.
That will block the user interaction with RecyclerView, making scroll not happen.

RecyclerView smoothScroll to position in the center. android

I am using a horizontal layout manager for my RecyclerView.
I need to make RecyclerView in the next way: when click on some item - make smoothScrool to that position and put that item in the center of RecyclerView (if it possible, for example, 10 item from 20).
So, I have no problem with smoothScrollToPosition(), but how to put item than in the center of RecyclerView???
Thanks!
Yes it's possible.
By implementing RecyclerView.SmoothScroller's method onTargetFound(View, State, Action).
/**
* Called when the target position is laid out. This is the last callback SmoothScroller
* will receive and it should update the provided {#link Action} to define the scroll
* details towards the target view.
* #param targetView The view element which render the target position.
* #param state Transient state of RecyclerView
* #param action Action instance that you should update to define final scroll action
* towards the targetView
*/
abstract protected void onTargetFound(View targetView, State state, Action action);
Specifically in LinearLayoutManager with LinearSmoothScroller:
public class CenterLayoutManager extends LinearLayoutManager {
public CenterLayoutManager(Context context) {
super(context);
}
public CenterLayoutManager(Context context, int orientation, boolean reverseLayout) {
super(context, orientation, reverseLayout);
}
public CenterLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
#Override
public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) {
RecyclerView.SmoothScroller smoothScroller = new CenterSmoothScroller(recyclerView.getContext());
smoothScroller.setTargetPosition(position);
startSmoothScroll(smoothScroller);
}
private static class CenterSmoothScroller extends LinearSmoothScroller {
CenterSmoothScroller(Context context) {
super(context);
}
#Override
public int calculateDtToFit(int viewStart, int viewEnd, int boxStart, int boxEnd, int snapPreference) {
return (boxStart + (boxEnd - boxStart) / 2) - (viewStart + (viewEnd - viewStart) / 2);
}
}
}
Improvements to the answer - there is no need to override the LinearLayoutManager
From the previous answer:
public class CenterSmoothScroller extends LinearSmoothScroller {
public CenterSmoothScroller(Context context) {
super(context);
}
#Override
public int calculateDtToFit(int viewStart, int viewEnd, int boxStart, int boxEnd, int snapPreference) {
return (boxStart + (boxEnd - boxStart) / 2) - (viewStart + (viewEnd - viewStart) / 2);
}
}
Here how to use it:
RecyclerView.LayoutManager lm = new GridLayoutManager(...): // or whatever layout manager you need
...
RecyclerView.SmoothScroller smoothScroller = new CenterSmoothScroller(recyclerView.getContext());
smoothScroller.setTargetPosition(position);
lm.startSmoothScroll(smoothScroller);
Just in case someone needs the Kotlin equivalent of the class in the accepted answer.
class CenterLayoutManager : LinearLayoutManager {
constructor(context: Context) : super(context)
constructor(context: Context, orientation: Int, reverseLayout: Boolean) : super(context, orientation, reverseLayout)
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes)
override fun smoothScrollToPosition(recyclerView: RecyclerView, state: RecyclerView.State, position: Int) {
val centerSmoothScroller = CenterSmoothScroller(recyclerView.context)
centerSmoothScroller.targetPosition = position
startSmoothScroll(centerSmoothScroller)
}
private class CenterSmoothScroller(context: Context) : LinearSmoothScroller(context) {
override fun calculateDtToFit(viewStart: Int, viewEnd: Int, boxStart: Int, boxEnd: Int, snapPreference: Int): Int = (boxStart + (boxEnd - boxStart) / 2) - (viewStart + (viewEnd - viewStart) / 2)
}
}
Based on #Boda's answer, if you want to control smooth scroll speed (for better animation) you can use below:
class CenterLayoutManager : LinearLayoutManager {
constructor(context: Context) : super(context)
constructor(context: Context, orientation: Int, reverseLayout: Boolean) : super(
context,
orientation,
reverseLayout
)
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : super(
context,
attrs,
defStyleAttr,
defStyleRes
)
override fun smoothScrollToPosition(
recyclerView: RecyclerView,
state: RecyclerView.State,
position: Int
) {
val centerSmoothScroller = CenterSmoothScroller(recyclerView.context)
centerSmoothScroller.targetPosition = position
startSmoothScroll(centerSmoothScroller)
}
private class CenterSmoothScroller(context: Context) : LinearSmoothScroller(context) {
override fun calculateDtToFit(
viewStart: Int,
viewEnd: Int,
boxStart: Int,
boxEnd: Int,
snapPreference: Int
): Int = (boxStart + (boxEnd - boxStart) / 2) - (viewStart + (viewEnd - viewStart) / 2)
override fun calculateSpeedPerPixel(displayMetrics: DisplayMetrics): Float {
return MILLISECONDS_PER_INCH / displayMetrics.densityDpi
}
}
companion object {
// This number controls the speed of smooth scroll
private const val MILLISECONDS_PER_INCH = 150f
}
}
Usage:
recyclerView.layoutManager = CenterLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false)
recyclerView.smoothScrollToPosition(selectedPosition)
since now(Feb 2019), I could easily use this code in ListView
(ListView)word_list_view.smoothScrollToPositionFromTop(your_item_index, center_position.y);
RecyclerView not verified, I guess would be the same.

Make fragment clickable when navigation drawer is opened

My problem is as follows: I lock the navigation drawer menu setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_OPEN) in the landscape mode of the tablet, but I need the fragment from the right to be active, so I can click it with navigation always opened. But I dont know how to do it. Please help.
There are a few things you need to do:
Disable the layout fading by setting a transparent color:
drawer.setScrimColor(Color.TRANSPARENT);
Lock the drawer
drawer.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_OPEN);
Create a custom drawer class which allows clicking through when in locked mode:
public class CustomDrawer extends DrawerLayout {
public CustomDrawer(Context context) {
super(context);
}
public CustomDrawer(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomDrawer(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
View drawer = getChildAt(1);
if (getDrawerLockMode(drawer) == LOCK_MODE_LOCKED_OPEN && ev.getRawX() > drawer.getWidth()) {
return false;
} else {
return super.onInterceptTouchEvent(ev);
}
}
}
Use this class in xml:
<com.example.myapplication.CustomDrawer
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- The main content view -->
</FrameLayout>
<ListView android:layout_width="100dp"
android:layout_height="match_parent"
android:layout_gravity="start"
android:background="#111"/>
</com.example.myapplication.CustomDrawer>
This is a tricky one. When the drawer is open, it intercepts your touch events which trigger the close of the drawer. In order to prevent that, you need to subclass your DrawerLayout and override the onInterceptTouchEvent method:
public class CustomDrawerLayout extends DrawerLayout
{
private View rightView;
private int mTouchSlop;
public CustomDrawerLayout (Context context)
{
this(context, null);
}
public CustomDrawerLayout (Context context, AttributeSet attrs)
{
this(context, attrs, 0);
}
public CustomDrawerLayout (Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(ViewConfiguration.get(context));
}
public void setRightView (View v)
{
this.rightView = v;
}
#Override
public boolean onInterceptTouchEvent (MotionEvent ev)
{
boolean result = super.onInterceptTouchEvent(ev);
if (rightView != null && isDrawerOpen(rightView))
{
DrawerLayout.LayoutParams layoutParams = (DrawerLayout.LayoutParams) rightView.getLayoutParams();
if (layoutParams.gravity == Gravity.END)
{
// This is true when the position.x of the event is happening on the left of the drawer (with gravity END)
if (ev.getX() < rightView.getX() && ev.getX() > mTouchSlop)
{
result = false;
}
}
}
return result;
}
}
This is my code working with a right drawer. I'm sure you can adapt this for your left drawer. You might also want to disable the shadow:
mDrawerLayout.setScrimColor(Color.TRANSPARENT);
Thanks for #Simas's solution!
I found if an item which user click is quite near drawerView, use ev.rawX is not appropriate. Furthermore, I add other gravity check to determinate interception.
class ContentTouchableDrawer #JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null
) : DrawerLayout(context, attrs) {
override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
val drawer: View = getChildAt(1)
logt("drawer : $drawer")
logt("drawer : width = ${drawer.width}")
logt("drawer : x = ${drawer.x}")
logt("drawer : eventRawX = ${ev.rawX}")
logt("drawer : eventX = ${ev.x}")
val drawerGravity = (drawer.layoutParams as LayoutParams).gravity
val result = when(drawerGravity){
Gravity.RIGHT, GravityCompat.END -> ev.x < drawer.x
Gravity.LEFT, GravityCompat.START -> ev.x > drawer.width
//Gravity.NO_GRAVITY
else -> false
}
return if (getDrawerLockMode(drawer) == LOCK_MODE_LOCKED_OPEN && result) {
false
} else {
super.onInterceptTouchEvent(ev)
}
}
}

Android KeyBoard.Key disable iconPreview of special keys?

I customize my own soft keyboard by implementing the KeyboardView.OnKeyboardActionListener interface.
When the keys are pressed, it will show a preview popup.
My problem is how to disable the preview popup for special keys such as SHIFT and DELETE?
I have tried to set the android:iconPreview attribute to null but it didn't work.
<Key
android:codes="-1"
android:keyIcon="#drawable/key_shift"
android:keyWidth="15%p"
android:isModifier="true"
android:isSticky="true"
android:keyEdgeFlags="left" />
Have any idea?
Thanks in advance!
First you must implement OnKeyboardActionListener
then use onPress() and onRelease() to control the preview popup like this:
public void onPress(int primaryCode) {
if (primaryCode==-2||primaryCode==-5||primaryCode==-4){
mInputView.setPreviewEnabled(false);
}
}
public void onRelease(int primaryCode) {
mInputView.setPreviewEnabled(true);
}
public void onCreate() {
mInputView.setPreviewEnabled(false);
}
public void onPress(int primaryCode) {
if (primaryCode==-1||primaryCode==-2||primaryCode==-5||primaryCode==-4){
} else {
mInputView.setPreviewEnabled(true);
}
}
public void onRelease(int primaryCode) {
mInputView.setPreviewEnabled(false);
}
The problem with the above solution, as commented is that
if I press other key (e.g. key A) and move my finger to the key SHIFT, the preview icon still popup
To counter this, I had to extend the KeyboardView class
Disclaimer - The following contains reflection api
Here is the modified Keyboard class
import android.content.Context
import android.inputmethodservice.KeyboardView
import android.os.Build
import android.support.annotation.RequiresApi
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import android.widget.TextView
import com.continental.testapplication.utils.dpToPx
import java.lang.reflect.Method
class ModifiedKeyboardView :KeyboardView{
constructor(context: Context, attrs: AttributeSet):super(context, attrs)
constructor(context: Context, attrs: AttributeSet, defStyleAttr:Int):super(context, attrs, defStyleAttr)
#RequiresApi(Build.VERSION_CODES.LOLLIPOP)
constructor(context: Context, attrs: AttributeSet, defStyleAttr:Int, defStyleRes:Int):
super(context, attrs, defStyleAttr, defStyleRes)
/**
* Return true, if preview is to be shown, false otherwise. If not implemented,
* the preview is going to be shown.....
*/
var keyPreviewIndexListener:((Int)->Boolean) ?= null
private val findKeyIndicesMethod:Method = KeyboardView::class.java.getDeclaredMethod(
"getKeyIndices",Int::class.java,Int::class.java, (IntArray::class).java).also {
it.isAccessible = true
}
private val previewText:TextView = KeyboardView::class.java.getDeclaredField(
"mPreviewText").let {
it.isAccessible = true
it.get(this) as TextView
}
override fun onTouchEvent(me: MotionEvent?): Boolean {
if(me == null) return super.onTouchEvent(me)
when(me.action){
MotionEvent.ACTION_DOWN -> isPreviewEnabled = true
MotionEvent.ACTION_MOVE -> {
val touchX = me.x - paddingLeft
var touchY = me.y.toInt() - paddingTop
val verticalCorrection = dpToPx(14f, context)
if (touchY >= -verticalCorrection)
touchY += verticalCorrection.toInt()
val keyIndex:Int = findKeyIndicesMethod.invoke(this, touchX.toInt(), touchY.toInt(), null) as Int
isPreviewEnabled = keyPreviewIndexListener?.invoke(keyIndex)?:true
if(!isPreviewEnabled){
previewText.visibility = View.INVISIBLE
}
}
}
return super.onTouchEvent(me)
}
}
Paste it as is.
Next, in the class where you are manipulating the keyboard,
keyboardView.keyPreviewIndexListener = {
it != spaceIndex && it != doneIndex && it != deleteIndex && it != `your_custom_index`
}
To find the indexes you can just do the following
doneIndex = keyboardView.keyboard.keys.indexOfFirst {
it.codes[0] == Keyboard.KEYCODE_DONE
}
This will prevent the movement. Please append the other solution also.
i.e
override fun onPress(primaryCode: Int) {
Log.e("onPress", primaryCode.toString())
checkAndActivatePreview(primaryCode)
}
override fun onRelease(primaryCode: Int) {
Log.e("onRelease", primaryCode.toString())
deactivatePreview()
}
private fun checkAndActivatePreview(primaryCode: Int) {
keyboard.isPreviewEnabled =
(primaryCode != `your_custom_code`
&& primaryCode != SPACE_KEY_CODE && primaryCode != Keyboard.KEYCODE_DELETE
&& primaryCode != Keyboard.KEYCODE_DONE)
}

Categories

Resources