Reputation: 615
I am trying to create custom buttons for my android application. These buttons are simply to add/edit/delete data on the screen. However, I would like to do some nice styling to the buttons but I am having some difficulties with the way android programming works.
The idea is to have a border of a given thickness (let's say 2dp) whose color is a gradient. This implies the center being transparent however and I consider this to be one of the main difficulties. Along with this, I would like the inside of the button to have an icon representing its function.
Currently, I have the gradient and its rounded edges like this:
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:gravity="top">
<shape android:shape="rectangle">
<gradient
android:startColor="@color/colorPrimaryDark"
android:centerColor="@color/colorAccent"
android:endColor="@android:color/white"
android:angle="270" />
<corners android:radius="20dp"/>
</shape>
</item>
</layer-list>
This corresponds to what I want color-wise. However, having a transparent center seems impossible to do in a drawable resource file.
Along with this issue, in my code, I have a ListView
above a LinearLayout
containing three buttons which this styling should eventually be applied to. However, I am unable to even change the background color of the buttons.
Here is the code representing the activity:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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=".AddPlayersActivity"
android:orientation="vertical"
android:paddingRight="10dp"
android:paddingLeft="10dp"
android:background="@drawable/background_gradient">
<TextView
android:id="@+id/add_players_activity_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="20dp"
android:fontFamily="@font/lato_bold"
android:text="@string/app_name"
android:textAlignment="center"
android:textColor="@android:color/white"
android:textSize="36sp" />
<TextView
android:layout_below="@+id/add_players_activity_title"
android:id="@+id/add_players_activity_current_players"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="@font/lato"
android:text="@string/current_players"
android:textColor="@android:color/white"
android:textSize="20sp" />
<ListView
android:id="@+id/add_players_activity_player_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/add_players_activity_current_players"
android:layout_above="@id/add_players_activity_button_layout" />
<LinearLayout
android:id="@+id/add_players_activity_button_layout"
android:layout_alignParentBottom="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center">
<Button
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginHorizontal="10dp"
android:text="Add"/>
<Button
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginHorizontal="10dp"
android:text="Edit"/>
<Button
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginHorizontal="10dp"
android:text="Delete" />
</LinearLayout>
</RelativeLayout>
None of the other StackOverflow questions have solved either of my issues. Even the most common "Set the style
attribute of the buttons" one.
I'm very open to solutions that could require external tools or other things so don't feel limited to internal functions. I do however feel that there has to be a way around this within the Android Studio.
Thanks in advance to anyone that takes time to look into this!
Here is an example as requested (Only difference is I will be using a drawable/image instead of text)
Upvotes: 3
Views: 4337
Reputation: 4951
I recommend creating a custom view for this. Creating a custom view would be highly beneficial to you as have mentioned you have many buttons. With a custom view, you can reuse your button with varying height, width, border width, colors, and corner radius.
Read more custom views here. You could also go through a series of codelabs starting this one.
attrs.xml (values/attrs.xml):
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Custom attributes for the button -->
<declare-styleable name="StyledButton">
<attr name="cornerRadius" format="dimension" />
<attr name="borderWidth" format="dimension" />
<attr name="startColor" format="color" />
<attr name="centerColor" format="color" />
<attr name="endColor" format="color" />
</declare-styleable>
</resources>
StyledButton.kt:
import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.LinearGradient
import android.graphics.Paint
import android.graphics.Path
import android.graphics.Shader
import android.util.AttributeSet
import androidx.appcompat.widget.AppCompatImageButton
import androidx.core.content.withStyledAttributes
class StyledButton @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : AppCompatImageButton(context, attrs, defStyleAttr) {
private var cornerRadius = 0f
private var borderWidth = 0f
private var startColor = 0
private var centerColor = 0
private var endColor = 0
private val path = Path()
private val borderPaint = Paint().apply {
style = Paint.Style.FILL
}
init {
//Get the values you set in xml
context.withStyledAttributes(attrs, R.styleable.StyledButton) {
borderWidth = getDimension(R.styleable.StyledButton_borderWidth, 10f)
cornerRadius = getDimension(R.styleable.StyledButton_cornerRadius, 10f)
startColor = getColor(R.styleable.StyledButton_startColor, Color.WHITE)
centerColor = getColor(R.styleable.StyledButton_centerColor, Color.RED)
endColor = getColor(R.styleable.StyledButton_endColor, Color.BLACK)
}
}
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
// Create and set your gradient here so that the gradient size is always correct
borderPaint.shader = LinearGradient(
0f,
0f,
width.toFloat(),
height.toFloat(),
intArrayOf(startColor, centerColor, endColor),
null,
Shader.TileMode.CLAMP
)
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
//Remove inner section (that you require to be transparent) from canvas
path.rewind()
path.addRoundRect(
borderWidth,
borderWidth,
width.toFloat() - borderWidth,
height.toFloat() - borderWidth,
cornerRadius - borderWidth / 2,
cornerRadius - borderWidth / 2,
Path.Direction.CCW
)
canvas.clipOutPath(path)
//Draw gradient on the outer section
path.rewind()
path.addRoundRect(
0f,
0f,
width.toFloat(),
height.toFloat(),
cornerRadius,
cornerRadius,
Path.Direction.CCW
)
canvas.drawPath(path, borderPaint)
}
}
Usage:
<com.example.andriod.myapplication.StyledButton
android:layout_width="100dp"
android:layout_height="40dp"
android:src="@drawable/ic_android"
android:scaleType="center"
android:layout_gravity="center"
app:borderWidth="2dp"
app:cornerRadius="20dp"
app:startColor="@color/colorPrimaryDark"
app:centerColor="@android:color/holo_red_dark"
app:endColor="@color/colorPrimary"/>
Result
Upvotes: 4
Reputation: 9872
I don't know if it is the best solution, but You can do it easy in this way:
gradient_back.xml
:<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="10dp" />
<gradient
android:endColor="@color/colorPrimary"
android:startColor="@color/colorAccent" />
</shape>
shape_front.xml
:<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="9dp" />
<solid android:color="@android:color/white" />
</shape>
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/gradient_back">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="3dp"
android:background="@drawable/shape_front"
android:text="WOW" />
</FrameLayout>
Result:
Version with the transparent body but not with gradient:
build.gradle
(Module: app) add:implementation "com.google.android.material:material:1.2.1"
res/values/styles
change parent style to:parent="Theme.MaterialComponents.Light.DarkActionBar"
<com.google.android.material.button.MaterialButton
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="WOW"
android:textColor="@color/textColor"
app:cornerRadius="10dp"
app:strokeWidth="2dp"
app:strokeColor="@color/colorAccent" />
Result:
Upvotes: 1