I've been exploring android development using Anko and Kotlin and had some trouble with the ratingbar, namely it size. I've tried to make it smaller using a custom style but themedRatingBar doesn't seem to work. So I've opted to make a custom ratingbar instead. I can't seem to make it work that way I want it to in that when I set it this way in the main activity:
starRatingView{
setRating(3)
}
It does not output a rating of 3 and instead will output the default rating, which is zero.
class StarRatingView: _LinearLayout {
lateinit var imageViewStars: List<ImageView>
private var starNum: Float = 0f
private var starSize: Int = 5
constructor(context: Context): super(context) {
initializeView()
}
fun initializeView() {
with(this) {
linearLayout {
relativeLayout {
linearLayout {
for (i in 1..starSize)
imageView(R.drawable.ratingbar_empty)
}
linearLayout {
for (i in 0..Math.round(starNum)) {
imageView(R.drawable.ratingbar_filled)
}
}
}
}
}
}
fun setSize(starSize: Int){
this.starSize = starSize
}
fun setRating(starNum: Float){
this.starNum = starNum
}
}
Above is the code that I use to create the custom RatinBar. Trying to avoid using XMLs as much as possible and use Anko instead.
if I can help here is a possible solution to show a custom RatingBar in a custom Alert using Anko library and Kotlin code...
var rateGave: String? = null
alert {
title = "Rate your experience"
customView {
linearLayout {
ratingBar {
numStars = 5 //here is to define the number of stars you want
rating = 4f //starting rate to show as the alert pop up
setOnRatingBarChangeListener { ratingBar, rating, fromUser ->
rateGave = rating.toString()
}
}
}
}
positiveButton("Rate") { rate() }
}.show()
}
fun rate() {
println(rateGave) //now it's just printing out the rate to show that it's working fine, but in this function you can mange all the operations you need with the rating value
}
Hope it can be usefull.
Have a nice day :)
Related
I am making a calculator app and for every button, i have to write button.setonclciklistner
I am tired of initializing the button after that on click method
please don't answer to implement on click method I already know that but is there any other method
Yes there is an option. If you do a calculator the gridView is perfect for that. Instead of making tons of onClickListener you can achieve that by multiple cases like in this example shown.
Here are some of the functions from my own calculator based app:
//normal backspace
private fun backSpace() {
form.bs.setOnClickListener {
inputField.text = inputField.text.dropLast(1)
UtilityMethods(context).playClick()
}
}
//long pressing the backspace will clear input field instantly
private fun longPressBS() {
form.bs.setOnLongClickListener {
inputField.text = null
true
}
}
//numeric keys are invoked here
private fun theNumericPad() {
for (i in 0 until 10) {
val resID = context.resources.getIdentifier("n$i", "id", context.packageName)
form.root.findViewById<Button>(resID).setOnClickListener {
if (inputField.text.toString() == "0") {
inputField.text = ""
}
if (inputField.text.toString() == ".") {
inputField.text = "0."
}
inputField.append(i.toString())
//clear custom type
if (!form.rb00.isChecked) {
form.ctField.text = ""
}
//play sound
UtilityMethods(context).playClick()
}
}
}
//the dot
private fun dot() {
form.dot.setOnClickListener {
if (inputField.text.toString() == "") {
inputField.append("0.")
} else {
inputField.append(".")
}
//clear custom type
if (!form.rb00.isChecked) {
form.ctField.text = ""
}
UtilityMethods(context).playClick()
}
}
And the function to play key tap sound is:
//play sounds
//normal key tap sound
fun playClick() {
val am = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
am.playSoundEffect(AudioManager.FX_KEY_CLICK, 1f)
}
Also to make it easy to understand, variable like form is the view binding of you keyboard layout.
I have the following code on my RecyclerView:
class TaskViewHolder(v: View) : RecyclerView.ViewHolder(v), View.OnClickListener
{
var mId: TextView = v.task_id
var mDescription: TextView = v.task_description
var mCard: CardView = v.task_card
var mView: View = v
}
override fun onBindViewHolder(holder: TaskViewHolder, position: Int)
{
// Initialize ViewHolder content
holder.mId.text = items[position].getID().toString()
holder.mDescription.text = items[position].getDescription()
holder.mTask = items[position]
for (elem in priorities)
{
if (elem.getID() == items[position].getPriority())
{
holder.mCard.setCardBackgroundColor(Color.parseColor("#c2c2c2"))
break
}
}
}
I don't know why, but CardView backgroundColor isn't changing.
If I use the following code, it works correctly:
holder.mCard.setCardBackgroundColor(ContextCompat.getColor(holder.mView.context, R.color.priority3))
What I should do to set CardBackgroundColor progrmatically?
First of all, You need to provide else part as transparent or another color to avoid color duplicate render issue. second, you have to pass context from your activity or fragment to adapter and that context will be used to get color like below.
if (elem.getID() == items[position].getPriority())
{
holder.mCard.setCardBackgroundColor(ContextCompat.getColor(mContext, [first color]))
} else {
holder.mCard.setCardBackgroundColor(ContextCompat.getColor(mContext,[second color]))
}
Try replacing "#c2c2c2" with "#ffc2c2c2" to make sure you provide a correct alpha for the background colour. On android, this extra byte added at the beginning represents the alpha of the colour:
#ffc2c2c2
ff: alpha
c2: red
c2: green
c2: blue
How do I use selectableButtonBackground attribute on a custom View that uses Anko's apply() method inside its constructor like the following structure?
class XPTO(context: Context) : CardView(context) {
init {
this.apply {
// I'd like to invoke selectableButtonBackground here
}
}
I've tried to do context.obtainStyledAttributes(arrayOf(R.attr.selectableItemBackground).toIntArray()).getDrawable(0) but with no success.
I just created an extension function to get the resource ids for attributes.
val Context.selectableItemBackgroundResource: Int get() {
return getResourceIdAttribute(R.attr.selectableItemBackground)
}
fun Context.getResourceIdAttribute(#AttrRes attribute: Int) : Int {
val typedValue = TypedValue()
theme.resolveAttribute(attribute, typedValue, true)
return typedValue.resourceId
}
This way you can also add more attributes if needed. Example to put it in anko:
frameLayout {
textView {
text = "Test"
backgroundResource = selectableItemBackgroundResource
isClickable = true
}
}
Don't forget the isClickable, else you won't see anything when you're clicking the textView
Another way to achieve this with Anko:
val backgroundResource = attr(R.attr.selectableItemBackgroundBorderless).resourceId
I'm using Anko in my Android project, but I don't know how can it reference the child views I created in the DSL when the referenced view is not at the same level where I reference it.
The following code works:
alert {
customView {
val input = textInputLayout {
editText {
hint = "Name"
textColor =resources.getColor(R.color.highlight)
}
}
positiveButton("OK") { "${input.editText.text}" }
}
}.show()
but the following code does not work:
alert {
customView {
val vertical = verticalLayout {
textView {
text = "Edit device name"
textColor = resources.getColor(R.color.highlight)
textSize = 24F
}
val input = textInputLayout {
editText {
hint = "Name"
textColor = resources.getColor(R.color.highlight)
}
}
}
positiveButton("OK") { "${vertical.input.editText.text}" } // Cannot resolve "input"
}
}.show()
As I see it there are two ways. The super hacky way would be to declare the positive button within the textInputLayout block. This is possible because you can access all outer scopes from within any nested scope and the positiveButton method is declared in the alert scope:
alert {
customView {
verticalLayout {
textInputLayout {
val editText = editText {
hint = "Name"
}
positiveButton("OK") { toast("${editText.text}") }
}
}
}
}.show()
The less hacky way would be to declare a variable that can be accessed from both scopes. However you would need to make it nullable since you can't initialize it immediately:
alert {
var editText: EditText? = null
customView {
verticalLayout {
textInputLayout {
editText = editText {
hint = "Name"
}
}
}
}
positiveButton("OK") { toast("${editText!!.text}") }
}.show()
I propose using findViewById()
alert {
customView {
val vertical = verticalLayout {
textView {
text = "Edit device name"
textSize = 24F
}
val input = textInputLayout {
editText {
id = R.id.my_id_resource // put your id here
hint = "Name"
}
}
}
positiveButton("OK") { "${(vertical.findViewById(R.id.my_id_resource) as? EditText)?.text}" }
}
}.show()
You can always elevate a view, passing the context vertical manually:
customView {
val vertical = verticalLayout {
textView {
text = "Edit device name"
textColor = resources.getColor(R.color.highlight)
textSize = 24F
}
}
val input = /*here:*/ vertical.textInputLayout {
editText {
hint = "Name"
textColor = resources.getColor(R.color.highlight)
}
}
positiveButton("OK") { "${input.editText.text}" }
}
I usually declare a view as a property in a class with lateinit modifier; this way it's not nullable and most of views are declared in one place, improving readability:
lateinit var toolbar: Toolbar
...
appBarLayout {
toolbar = toolbar {}.lparams(width = matchParent, height = matchParent)
}.lparams(width = matchParent)
...
setSupportActionBar(toolbar)
Probably the best way is to use Android IDs for the elements you need to reference later, and the find<T : View>(Int) : T function. This allows you to reference them from anywhere, as long as the view still exists, and you have access to the application/activity scope.
See the Anko Documentation for details
Example case: Dynamically adding buttons to an existing view
verticalLayout {
id = R.id.button_container
}
//note that the code below here may be executed anywhere after the above in your onCreate function
//verticalLayout is a Anko subclass of LinearLayout, so using the android class is valid.
val buttonContainer = find<LinearLayout>(R.id.button_container)
val containerContext = AnkoContext.Companion.create(ctx, buttonContainer)
val button = ctx.button {
text = "click me"
onClick = { toast("created with IDs!") }
}
buttonContainer.addView(button.createView(containerContext, buttonContainer))
Currently, I'm trying to develop an app.
and I don't know how to change the Toast font. .
final OnClickListener clickListener = new OnClickListener() {
public void onClick(View v) {
try {
Toast.makeText(nova.this,"Hello", 500000).show();
}
catch (Exception e) {
Toast.makeText(nova.this,"Exception:" +e, 500000);
}
}
};
I want to change the text "Hello" with custom font I've tried with TypeFace.
and Then, I want to set a variable at the place "TextClicked" .. I've tried with a local variable .. but it doesn't work
any help with example source code will be really great for me.
The answer is here: https://stackoverflow.com/a/13231981
After refactoring a little:
Toast toast = Toast.makeText(context, R.string.yummyToast, Toast.LENGTH_SHORT);
LinearLayout toastLayout = (LinearLayout) toast.getView();
TextView toastTV = (TextView) toastLayout.getChildAt(0);
toastTV.setTextSize(30);
toast.show();
This worked for me like a charm!
From the official documentation:
Create your custom ToastView
If a simple text message isn't enough, you can create a customized layout for your toast notification. To create a custom layout, define a View layout, in XML or in your application code, and pass the root View object to the setView(View) method.
Following the link to the official Google Documentation will provide examples.
You can use a SpannableString to set the font:
Typeface font = Typeface.createFromAsset(getAssets(), "fonts/ATaha.ttf");
SpannableString efr = new SpannableString("Toast font changed!");
efr.setSpan(new TypefaceSpan(font), 0, efr.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
Toast.makeText(this, efr, Toast.LENGTH_SHORT).show();
A custom Span class that has a specific Typeface set:
public class TypefaceSpan extends MetricAffectingSpan {
private Typeface mTypeface;
public TypefaceSpan(Typeface typeface) {
mTypeface = typeface;
}
#Override
public void updateMeasureState(TextPaint p) {
p.setTypeface(mTypeface);
p.setFlags(p.getFlags() | Paint.SUBPIXEL_TEXT_FLAG);
}
#Override
public void updateDrawState(TextPaint tp) {
tp.setTypeface(mTypeface);
tp.setFlags(tp.getFlags() | Paint.SUBPIXEL_TEXT_FLAG);
}
}
Unfortunately the code on the Java page is bugged. Here is a link to a working function you can implement that gives you the text (I know, because I tested it), and with a little ingenuity, could be expanded to pass arguments for size, color, etc...
Toast Font size function here
Kotlin function:
fun makeLargeTextToast(text: CharSequence): Toast {
return Toast.makeText(applicationContext, text, Toast.LENGTH_LONG).also {
val toastLayout = it.view as LinearLayout
val toastTV = toastLayout.getChildAt(0) as TextView
toastTV.textSize = 30f
}
}
Use it as:
makeLargeTextToast("text message").show()
I used this solution in kotlin
in CustomView or Fragment
fun persianToast(message: String): Toast {
return Toast.makeText(context, message, Toast.LENGTH_SHORT).also {
val view = it.view as LinearLayout
val tv = view.getChildAt(0) as TextView
val typeFace = Typeface.createFromAsset(context?.assets, MyApplication.getFont(MyApplication.LIGHT_FONT))
tv.typeface = typeFace
}
}
MyApplication class :
class MyApplication : Application() {
companion object {
const val NORMAL_FONT = 0
const val BOLD_FONT = 1
const val MEDIUM_FONT = 2
const val LIGHT_FONT = 3
const val ULTRA_LIGHT_FONT = 4
#JvmStatic
fun getFont(type: Int): String {
return when (type) {
LIGHT_FONT -> "font/fontLight.ttf"
BOLD_FONT -> "font/fontBold.ttf"
MEDIUM_FONT -> "font/fontMedium.ttf"
ULTRA_LIGHT_FONT -> "font/fontUltraLight.ttf"
else -> "font/fontNormal.ttf"
}
}
}
used in fragment:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
Toast(context)
persianToast("javid sattar").show()
}
good luck!!