I have a Textview which I set the property drawableRight from my xml file, now I will like to change the drawable programatically.
I want when clicked, drawable icon/image should change from let say btn_up to btn_down.
Below is what I tried so far:
Class:
requireText.SetOnClickListener {
requireText.drawableRight = resources.getDrawable(R.drawable.btn_up)
}
xml:
<TextView
android:id="#+id/requireText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:background="#null"
android:clickable="true"
android:drawableRight="#drawable/btn_down"/>
How do I solve this? Thanks in advance.
There is no .drawableRight or Left, not yet for now, you can try below code:
class SampleActivity : AppCompatActivity() {
var up = true
var drawable: Drawable ?=null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_signup)
userName.setOnClickListener {
if(up){
up = false
drawable = resources.getDrawable(com.exolve.eros.R.drawable.sampleDownDrawable)
}else{
up = true
drawable = resources.getDrawable(com.exolve.eros.R.drawable.sampleUpDrawable)
}
drawable!!.setBounds(0, 0, 10, 10)
userName.setCompoundDrawables(drawable, null, null, null)
}
}
}
An update to the fact that you want to switch the drawable on click.
Try
var isClicked = false // declare this at top
requireText.setOnClickListener{
requireText.setCompoundDrawablesWithIntrinsicBounds(0, 0,
if(!isClicked) R.drawable.btn_up else R.drawable.btn_down, 0)
isClicked = !isClicked
}
try this code to set drawable right in kotlin class
val my_text_view = findViewById(R.id.my_text_view)
my_text_view.setOnClickListener(View.OnClickListener { my_text_view.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.ic_arrow_upward_black_24dp, 0) })
Related
Lets say we have a simple EditText and I want to change the cursor(caret) to some other color, before we were use reflections to get access to the private fields, but with introduction of Android API Q(29), we can now use textCursorDrawable to set the drawable for the blinking cursor.
Here is the xml code of the EditText
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<EditText
android:id="#+id/editText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Test"
android:textSize="30sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Now we can use a WrapDrawable to wrap a ColorDrawable, that will be set as textCursorDrawable value of the EditText, in order for us to change the cursor color.
Here is the code for the WrapDrawable:
class WrapDrawable(color: Int) : Drawable() {
private var drawable = ColorDrawable(color)
#ColorInt
var color: Int = color
set(value) {
field = value
drawable = ColorDrawable(value)
}
override fun setBounds(left: Int, top: Int, right: Int, bottom: Int) {
super.setBounds(left, top, right, bottom)
drawable.setBounds(left, top, right, bottom)
}
override fun getConstantState(): ConstantState? {
return drawable.constantState
}
override fun setAlpha(alpha: Int) {
drawable.alpha = alpha
}
override fun setColorFilter(colorFilter: ColorFilter?) {
drawable.colorFilter = colorFilter
}
override fun getOpacity(): Int {
return drawable.alpha
}
override fun draw(canvas: Canvas) {
drawable.draw(canvas)
}
override fun getIntrinsicWidth(): Int {
return drawable.bounds.width()
}
override fun getIntrinsicHeight(): Int {
return drawable.bounds.height()
}
}
In the code below, we change the color of the cursor twice once to Color.RED and second time to Color.BLUE, now we should expect to have a BLUE cursor.
But the problem is that once textCursorDrawable is set, we cannot change it even if we try nullify it.
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val text = findViewById<EditText>(R.id.editText)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
// set the cursor color to RED
text.textCursorDrawable = WrapDrawable(Color.RED).apply {
setBounds(0, 0, 5, text.lineHeight)
}
// set the cursor color to BLUE !!! NOT WORKING !!!
text.textCursorDrawable = WrapDrawable(Color.BLUE).apply {
setBounds(0, 0, 5, text.lineHeight)
}
}
}
}
So my question is how can we reassign the textCursorDrawable value multiple times?
I have found a workaround by updating the already existing textCursorDrawable value, and changing the ColorDrawable using the color variable.
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val text = findViewById<EditText>(R.id.editText)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
// set the cursor color to RED
text.textCursorDrawable = WrapDrawable(Color.RED).apply {
setBounds(0, 0, 5, text.lineHeight)
}
// set the cursor color to BLUE
text.textCursorDrawable?.let {
if (it is WrapDrawable) {
it.color = Color.BLUE
it.setBounds(0, 0, 5, text.lineHeight)
}
}
}
}
}
The documentation for setTextCursorDrawable() states:
Note that any change applied to the cursor Drawable will not be visible until the cursor is hidden and then drawn again.
I have taken a quick look through the TextView and EditText code and haven't determined how to make the change you want. I am not saying that it can't be done; I just don't see it.
Instead, try making a change to your WrapDrawable like this:
(text.textCursorDrawable as WrapDrawable).apply {
color = Color.BLUE
setBounds(0, 0, 5, text.lineHeight)
}
This will work and will save the instantiation of a new WrapDrawable.
Update
Can't prove a negative, but it looks like the cursor drawable can't be replaced once set. The following is the reasoning.
For API 31, there are only two places within the TextView code where the cursor drawable is set. The private scope of mCursorDrawable will restrict outside access.
In TextView.java:
private Drawable mCursorDrawable;
public void setTextCursorDrawable(#Nullable Drawable textCursorDrawable) {
mCursorDrawable = textCursorDrawable;
mCursorDrawableRes = 0;
if (mEditor != null) {
mEditor.loadCursorDrawable();
}
}
#Nullable public Drawable getTextCursorDrawable() {
if (mCursorDrawable == null && mCursorDrawableRes != 0) {
mCursorDrawable = mContext.getDrawable(mCursorDrawableRes);
}
return mCursorDrawable;
}
It is the text editor class that the cursor is drawn and it reaches back into the TextView to get the drawable that will be used.
In Editor.java:
Drawable mDrawableForCursor = null;
private void drawCursor(Canvas canvas, int cursorOffsetVertical) {
final boolean translate = cursorOffsetVertical != 0;
if (translate) canvas.translate(0, cursorOffsetVertical);
if (mDrawableForCursor != null) {
mDrawableForCursor.draw(canvas);
}
if (translate) canvas.translate(0, -cursorOffsetVertical);
}
void loadCursorDrawable() {
if (mDrawableForCursor == null) {
mDrawableForCursor = mTextView.getTextCursorDrawable();
}
}
loadCursorDrawable is the only place that mDrawableForCursor is set so, once it is defined, it can't be changed. Since it can't be changed, it can't be set to null to pick up a new cursor drawable that may be defined in the text view.
So, the long and the short of this is that the cursor can be changed in TextView but cannot be propagated to the editor.
I have a Seekbar:
<SeekBar
android:id="#+id/sw_lock"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="60dp"
android:layout_marginTop="20dp"
android:layout_marginEnd="60dp"
android:max="100"
android:thumb="#drawable/ic_thumb"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/lbl_device_status" />
Im using databinding and all is working fine. The problem comes when i want to change the thumb color.
In a fragment, i have a vertical linear layout containing 0...n views which contains this seekbar
response.observe(viewLifecycleOwner, Observer { list ->
activity?.run {
list.forEach { element ->
val mView = MyView(this)
mView.layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
mView.bind(element)
mView.didUnlock = { view, unlocked, element ->
//DO STUFF
}
binding.container.addView(mView)
}
}
})
This is working fine. I have n instances and each instance works properly.
Now, i want to change the thumb color when the progress change so i have:
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
changeColor(binding.swLock.progress > 80)
}
and changeColor method is just like this:
private fun changeColor(active: Boolean) {
val color = if(active) {
R.color.colorAccent
} else {
R.color.text_main
}
binding.swLock.progressDrawable.setTint(getColor(color))
binding.swLock.thumb.setTint(getColor(color))
}
And here comes the weird thing, the progressDrawable changes in each instance, the thumb changes for all instances. What am i doing wrong?
Thanks and regards
Send seekbar to changeColor fun like this:
private fun changeColor(active: Boolean, seekBar : SeekBar) {
val color = if(active) {
R.color.colorAccent
} else {
R.color.text_main
}
seekBar.thumb.setTint(getColor(color))
}
and call changeColor:
changeColor(binding.swLock.progress > 80, seekBar)
Good luck...
it seems that adding tint to the drawable is modifying all the instances of that drawable (if it makes any sense), so i added also the "active" drawable and changed the changeColor function to
private fun changeColor(active: Boolean) {
val thumb = if(active) {
R.drawable.ic_thumb_active
} else {
R.drawable.ic_thumb
}
val color = if(active) {
R.color.colorAccent
} else {
R.color.text_main
}
binding.swLock.progressDrawable.setTint(getColor(color))
binding.swLock.thumb = resources.getDrawable(thumb, null)
}
i just have to make this cleaner but its working now
EditText right drawable doesn't update after showError method gets called. I have tried to set setError to null, set right drawable to null but nothing helps.
Drawable right = DrawableUtils.getDrawable(context,R.drawable.eye_look);
right.setBounds(new Rect(0, 0, rigth.getIntrinsicWidth(), right.getIntrinsicHeight()));
myEditText.setError(null, null);
myEditText.setCompoundDrawablesWithIntrinsicBounds(DrawableUtils.getDrawable(context,R.drawable.pass_look), null, right, null);
Any ideas ?
Code example for checking:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
vEditText.error = "Some error"
vButton.setOnClickListener {
vEditText.error = null
}
vEditText.setOnTouchListener(object : OnTouchListener {
override fun onTouch(p0: View?, event: MotionEvent?): Boolean {
val DRAWABLE_LEFT = 0
val DRAWABLE_TOP = 1
val DRAWABLE_RIGHT = 2
val DRAWABLE_BOTTOM = 3
if (event?.action == MotionEvent.ACTION_UP) {
if (event.rawX >= (vEditText.right - vEditText.compoundDrawables[DRAWABLE_RIGHT].bounds.width())) {
vEditText.setCompoundDrawablesWithIntrinsicBounds(null, null, ContextCompat.getDrawable(vEditText.context,android.R.drawable.btn_star_big_on), null)
vEditText.error = null
return true
}
}
return false
}
})
}
}
First decorate the EditText with your desired Drawable.
Drawable right = DrawableUtils.getDrawable(context,R.drawable.eye_look);
right.setBounds(new Rect(0, 0, rigth.getIntrinsicWidth(), right.getIntrinsicHeight()));
myEditText.setCompoundDrawablesWithIntrinsicBounds(DrawableUtils.getDrawable(context,R.drawable.pass_look), null, right, null);
...
Now whenever need to show error just use
myEditText.setError("Your error message");
And hide error drawable like below
myEditText.setError(null);
Which actually hide the error drawable and show your drawable. No need to do anything more.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
I have update your code, check now
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//This is important to set here
vEditText.setCompoundDrawablesWithIntrinsicBounds(null, null, ContextCompat.getDrawable(vEditText.context,android.R.drawable.btn_star_big_on), null)
vEditText.error = "Some error"
vButton.setOnClickListener {
vEditText.error = null
}
}
Now type something on vEditText or click vButton and check.
Try this:
myEditText.setError(null);
myEditText.setErrorEnabled(false);
myEditText.setCompoundDrawablesWithIntrinsicBounds(ContextCompat.getDrawable(context,R.drawable.icon), null, ContextCompat.getDrawable(context,R.drawable.icon), null);
I am a beginner in Kotlin .I am not too much familier with this language. I am making one example and playing with code. I Just want to set runtime margin to any view. I also trying to google it but not getting any proper solution for this task.
Requirement
Set runtime margin to any View.
Description
I have taking one xml file which is contain on Button and I want to set runtime margin to this button.
Code
I also try below thing but it's not work.
class MainActivity : AppCompatActivity() {
//private lateinit var btnClickMe: Button
//var btnClickMe=Button();
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//For setting runtime text to any view.
btnClickMe.text = "Chirag"
//For getting runtime text to any view
var str: String = btnClickMe.text as String;
//For setting runtimer drawable
btnClickMe.background=ContextCompat.getDrawable(this,R.drawable.abc_ab_share_pack_mtrl_alpha)//this.getDrawable(R.drawable.abc_ab_share_pack_mtrl_alpha)
/*
//For Setting Runtime Margine to any view.
var param:GridLayout.LayoutParams
param.setMargins(10,10,10,10);
btnClickMe.left=10;
btnClickMe.right=10;
btnClickMe.top=10;
btnClickMe.bottom=10;
*/
// Set OnClick Listener.
btnClickMe.setOnClickListener {
Toast.makeText(this,str,5000).show();
}
}
}
activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:orientation="vertical"
tools:context="chirag.iblazing.com.stackoverflowapp.MainActivity"
android:layout_height="match_parent">
<Button
android:id="#+id/btnClickMe"
android:text="Click Me"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
How can I proceed?
You need to get the layoutParams object from button and cast it to ViewGroup.MarginLayoutParams (which is a parent class of LinearLayout.LayoutParams, RelativeLayout.LayoutParams and others and you don't have to check which is btnClickMe's actual parent) and set margins to whatever you want.
Check following code:
val param = btnClickMe.layoutParams as ViewGroup.MarginLayoutParams
param.setMargins(10,10,10,10)
btnClickMe.layoutParams = param // Tested!! - You need this line for the params to be applied.
This is how I would like to do in Kotlin -
fun View.margin(left: Float? = null, top: Float? = null, right: Float? = null, bottom: Float? = null) {
layoutParams<ViewGroup.MarginLayoutParams> {
left?.run { leftMargin = dpToPx(this) }
top?.run { topMargin = dpToPx(this) }
right?.run { rightMargin = dpToPx(this) }
bottom?.run { bottomMargin = dpToPx(this) }
}
}
inline fun <reified T : ViewGroup.LayoutParams> View.layoutParams(block: T.() -> Unit) {
if (layoutParams is T) block(layoutParams as T)
}
fun View.dpToPx(dp: Float): Int = context.dpToPx(dp)
fun Context.dpToPx(dp: Float): Int = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, resources.displayMetrics).toInt()
now we just have to call this on a view like
textView.margin(left = 16F)
Here's a useful Kotlin extension method:
fun View.setMargins(
left: Int = this.marginLeft,
top: Int = this.marginTop,
right: Int = this.marginRight,
bottom: Int = this.marginBottom,
) {
layoutParams = (layoutParams as ViewGroup.MarginLayoutParams).apply {
setMargins(left, top, right, bottom)
}
}
Use it like this:
myView.setMargins(
top = someOtherView.height
bottom = anotherView.height
)
EDIT: the solution is similar to the answer from Hitesh, but I'm using the (original) ViewGroup.setMargins in pixels. Of course you can make your own setMarginsDp variant based on these examples, or use Hitesh's dpToPx extension before calling my implementation. Whichever solution you choose depends on your own taste.
Also take note that my solution (re)sets all margins, although this won't be an issue in most cases.
If you want to change specific margin like top or bottom you can use below code with Data binding .
#BindingAdapter("android:layout_marginTop")
#JvmStatic
fun setLayoutMarginTop(view: View, marginTop: Float) {
val layoutParams = view.layoutParams as ViewGroup.MarginLayoutParams
layoutParams.topMargin = marginTop.toInt()
view.layoutParams = layoutParams
}
and in .xml file you can write like below code
<ImageView
android:id="#+id/imageView3"
android:layout_width="#dimen/_15dp"
android:layout_height="#dimen/_15dp"
android:layout_marginTop="#{homeViewModel.getLanguage() ? #dimen/_14dp : #dimen/_32dp }"
android:contentDescription="#string/health_indicator"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/imageView1"
app:layout_constraintEnd_toStartOf="#+id/textView3"
android:src="#{ homeViewModel.remoteStatusVisible ? #drawable/green_rectangle : #drawable/gray_rectangle}"/>
Here is another sample of CardView
myCardView.elevation = 0F
myCardView.radius = 0F
val param = (myCardView.layoutParams as ViewGroup.MarginLayoutParams).apply {
setMargins(0,0,0,0)
}
myCardView.layoutParams = param
I'm trying to add a spinner inside an alert using anko. My code so far looks like this:
alert(getString(R.string.alert)) {
positiveButton("Cool") { toast("Yess!!!") }
customView {
linearLayout {
textView("I'm a text")
padding = dip(16)
orientation = LinearLayout.VERTICAL
spinner(R.style.Widget_AppCompat_Spinner) {
id = R.id.spinner_todo_category
prompt = "Select a Category"
}
}
}
}.show()
but I get compilation errors because apparently that's not how to call a spinner. I've been looking at the docs (Anko GitHub Wiki) but it says nothing about spinners.
Thanks in advance
One solution :
class AddActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val items = listOf(Friend("bla","bla",50),Friend("bla","bla",50));
val adapterFriends = ArrayAdapter(this,R.layout.mon_spinner,items)
verticalLayout {
val friends = spinner { adapter = adapterFriends }
val wine = editText()
button("Say Hello") {
onClick { toast("Hello, ${wine.text}!") }
}
}
}
}
with this layout (mon_spinner.xml) :
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="10dp"
android:textSize="14sp"
android:textColor="#color/colorPrimary"
android:spinnerMode="dialog"
android:text="XXX"
/>
It's all right !!
Try this in your AnkoComponent:
spinner {
adapter = ArrayAdapter.createFromResource(
ctx,
R.array.your_string_array,
android.R.layout.simple_spinner_dropdown_item)
}