Helo every one. I want to create a fragment that contains a bottom navigation bar. Bottom navigation is working but seeming wrong place in my fragment layout. An also this codes are working so good in activity but not in fragment.
Here is my fragment that I wanted to show bottom navigation bar.
class ParametreIslemlerFragment:Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
super.onCreateView(inflater, container, savedInstanceState);
val mContext: Context = activity!!.applicationContext;
val view:View = inflater.inflate(R.layout.fragment_parametre_islemler,container,false);
val navBottomMenu:CurvedBottomNavigationView = view.findViewById(R.id.parametreBottomNavigation);
initNavBottomPreferences(navBottomMenu);
//fragmentManager?.beginTransaction()?.replace(R.id.parametreFragmentTutucu,FragmentBirinci())?.commit();
navBottomMenu.setOnNavigationItemSelectedListener { menuItem ->
true;
}
return view;
}
private fun initNavBottomPreferences(navBottomMenu:CurvedBottomNavigationView){
navBottomMenu.inflateMenu(R.menu.parametre_menu_nav_items);
navBottomMenu.labelVisibilityMode = LabelVisibilityMode.LABEL_VISIBILITY_LABELED;
navBottomMenu.menu.getItem(1).isVisible = false;
}
}
This fragment uses above layout;
<?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"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="#+id/parametreFragmentTutucu"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="30dp"
app:layout_constraintBottom_toTopOf="#+id/parametreBottomNavigation"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
</FrameLayout>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="#+id/floatingActionButton3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="true"
app:layout_constraintEnd_toEndOf="parent"
android:backgroundTint="#color/colorAccent"
android:layout_marginTop="1dp"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/parametreFragmentTutucu"
app:srcCompat="#drawable/ic_add_24dp" />
<com.mesutemre.kutuphanesistemi.customcomponents.CurvedBottomNavigationView
android:id="#+id/parametreBottomNavigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">
</com.mesutemre.kutuphanesistemi.customcomponents.CurvedBottomNavigationView>
</androidx.constraintlayout.widget.ConstraintLayout>
CurvedBottomNavigation is a custom view and extends BottomNavigationView. When I run these codes ;
The codes are working in activity , why don't seems like activity layout in fragment?
Source code of CurvedBottomNavigationView is ;
class CurvedBottomNavigationView(context: Context, attrs:
AttributeSet)
:BottomNavigationView(context, attrs) {
private lateinit var mPath: Path;
private lateinit var mPaint: Paint;
private val CURVE_CIRCLE_RADIUS = 110 / 2;
private var mFirstCurveStartPoint: Point = Point();
private var mFirstCurveEndPoint: Point = Point();
private var mFirstCurveControlPoint1: Point = Point();
private var mFirstCurveControlPoint2: Point = Point();
private var mSecondCurveStartPoint: Point = Point();
private var mSecondCurveEndPoint: Point = Point();
private var mSecondCurveControlPoint1: Point = Point();
private var mSecondCurveControlPoint2: Point = Point();
private var mNavigationBarWidth: Int = 0;
private var mNavigationBarHeight: Int = 0;
init {
this.init();
}
private fun init(): Unit {
mPath = Path();
mPaint = Paint();
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
val colors = IntArray(3);
colors[0] = ContextCompat.getColor(
context,
R.color.bottom_start_color
);
colors[1] = ContextCompat.getColor(
context,
R.color.bottom_center_color
);
colors[2] = ContextCompat.getColor(
context,
R.color.bottom_end_color
);
val positions = FloatArray(3); //floatArrayOf(0f, 0.3f, 0.6f);
positions[0] = 0f;
positions[1] = 0.2f;
positions[2] = 0.4f;
mPaint.setShader(
LinearGradient(
0f, 0f, measuredWidth.toFloat(), 0f,
colors,
positions,
Shader.TileMode.CLAMP
)
);
mPaint.setShader(
LinearGradient(
0f, 0f, 0f, 250f,
colors, positions,
Shader.TileMode.MIRROR
)
);
//mPaint.setColor(ContextCompat.getColor(getContext(), R.color.primaryTextColor));
setBackgroundColor(Color.TRANSPARENT);
//background = resources.getDrawable(R.drawable.nav_bottom_background);
}
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh);
mNavigationBarWidth = getWidth();
mNavigationBarHeight = getHeight();
mFirstCurveStartPoint.set(
(mNavigationBarWidth / 2) - (CURVE_CIRCLE_RADIUS * 2) - (CURVE_CIRCLE_RADIUS / 3),
0
);
mFirstCurveEndPoint.set(
mNavigationBarWidth / 2,
CURVE_CIRCLE_RADIUS + (CURVE_CIRCLE_RADIUS / 4)
);
// same thing for the second curve
mSecondCurveStartPoint = mFirstCurveEndPoint;
mSecondCurveEndPoint.set(
(mNavigationBarWidth / 2) + (CURVE_CIRCLE_RADIUS * 2) + (CURVE_CIRCLE_RADIUS / 3),
0
);
mFirstCurveControlPoint1.set(
mFirstCurveStartPoint.x + CURVE_CIRCLE_RADIUS + (CURVE_CIRCLE_RADIUS / 4),
mFirstCurveStartPoint.y
);
// the coordinates (x,y) of the 2nd control point on a cubic curve
mFirstCurveControlPoint2.set(
mFirstCurveEndPoint.x - (CURVE_CIRCLE_RADIUS * 2) + CURVE_CIRCLE_RADIUS,
mFirstCurveEndPoint.y
);
mSecondCurveControlPoint1.set(
mSecondCurveStartPoint.x + (CURVE_CIRCLE_RADIUS * 2) - CURVE_CIRCLE_RADIUS,
mSecondCurveStartPoint.y
);
mSecondCurveControlPoint2.set(
mSecondCurveEndPoint.x - (CURVE_CIRCLE_RADIUS + (CURVE_CIRCLE_RADIUS / 4)),
mSecondCurveEndPoint.y
);
mPath.reset();
mPath.moveTo(0F, 0F);
mPath.lineTo(mFirstCurveStartPoint.x.toFloat(), mFirstCurveStartPoint.y.toFloat());
mPath.cubicTo(
mFirstCurveControlPoint1.x.toFloat(), mFirstCurveControlPoint1.y.toFloat(),
mFirstCurveControlPoint2.x.toFloat(), mFirstCurveControlPoint2.y.toFloat(),
mFirstCurveEndPoint.x.toFloat(), mFirstCurveEndPoint.y.toFloat()
);
mPath.cubicTo(
mSecondCurveControlPoint1.x.toFloat(), mSecondCurveControlPoint1.y.toFloat(),
mSecondCurveControlPoint2.x.toFloat(), mSecondCurveControlPoint2.y.toFloat(),
mSecondCurveEndPoint.x.toFloat(), mSecondCurveEndPoint.y.toFloat()
);
mPath.lineTo(mNavigationBarWidth.toFloat(), 0F);
mPath.lineTo(mNavigationBarWidth.toFloat(), mNavigationBarHeight.toFloat());
mPath.lineTo(0F, mNavigationBarHeight.toFloat());
mPath.close();
}
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
super.onLayout(changed, left, top, right, bottom)
}
#SuppressLint("ResourceAsColor")
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas);
canvas?.drawPath(mPath, mPaint);
}
}
You are ignoring attributes and def styles. Extend view classes with #JvmOverloads annotation
class CurvedBottomNavigationView #JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : BottomNavigationView(context, attrs, defStyleAttr) {
// custom class body
}
Related
I would like to make such background for my textView:
I managed to make only with outer semi circle:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#color/p_color" />
<corners android:radius="24dp" />
</shape>
which is not good for me. I also tried to work with states list but didn't succeed with it.
Hello my Friend,
What you need will not create by shape Drawable, you need a custome view
in fact you need some trick to make your desired shape by custom views and use it inside a constraintlayout as background of your TextView
I coded for you by kotlin(if you need java inform me):
first:
make a file and name it CustomeBg copy below code put in that:
import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import android.view.View
class CustomeBg #JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
private var W = 0.0f
private var H = 0.0f
/////main body Rect and Paint
private var bodyRect = Rect()
private var bodyRectPaint = Paint(Paint.ANTI_ALIAS_FLAG)
//right half circle Rect and Paint
private var rightCircleRect = RectF()
private var rightCirclePaint = Paint(Paint.ANTI_ALIAS_FLAG)
//left half circle Rect and Paint
private var leftCircleRect = RectF()
private var leftCirclePaint = Paint(Paint.ANTI_ALIAS_FLAG)
init {
}
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
W = w.toFloat()
H = h.toFloat()
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
// drawRect(canvas)
var left = 0
var top = 0
var right = W - RADIUS_RIGHT
var bottom = H
bodyRectPaint.apply {
color = BODY_COLOR_RECT
style = Paint.Style.FILL
}
bodyRect.set(left, top, right.toInt(), bottom.toInt())
canvas.drawRect(bodyRect, bodyRectPaint)
// Draw Left half-Circle
var left_l_circle = -RADIUS_LEFT
var top_l_circle = 0f
var right_l_circle = RADIUS_LEFT
var bottom_l_circle = H
leftCirclePaint.apply {
color = LEFT_CIRCLE_COLOR
style = Paint.Style.FILL
}
leftCircleRect = RectF(left_l_circle, top_l_circle, right_l_circle, bottom_l_circle)
canvas.drawArc(leftCircleRect, -90f, 180f, true, leftCirclePaint)
// Draw Right half-Circle
var left_r_circle = (bodyRect.right) - RADIUS_RIGHT
var top_r_circle = 0f
var right_r_circle = bodyRect.right + RADIUS_RIGHT
var bottom_r_circle = H
rightCirclePaint.apply {
color = RIGHT_CIRCLE_COLOR
style = Paint.Style.FILL
}
rightCircleRect = RectF(left_r_circle, top_r_circle, right_r_circle, bottom_r_circle)
canvas.drawArc(rightCircleRect, -90f, 180f, true, rightCirclePaint)
}
companion object {
private val BODY_COLOR_RECT = Color.parseColor("#00534b")
private val LEFT_CIRCLE_COLOR = Color.parseColor("#ffffff")
private val RIGHT_CIRCLE_COLOR = Color.parseColor("#00534b")
private val RADIUS_LEFT = 100f
private val RADIUS_RIGHT = 100f
}
}
second:
then in your main_activity layout put below XML codes:
<?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">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:layout_editor_absoluteX="51dp"
tools:layout_editor_absoluteY="95dp">
<com.example.junk2.CustomeBg
android:id="#+id/itemSettingBg"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginStart="30dp"
android:layout_marginEnd="30dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
</com.example.junk2.CustomeBg>
<TextView
android:id="#+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hallo ich bin Custome"
android:textColor="#FFFFFF"
app:layout_constraintBottom_toBottomOf="#+id/itemSettingBg"
app:layout_constraintEnd_toEndOf="#+id/itemSettingBg"
app:layout_constraintStart_toStartOf="#+id/itemSettingBg"
app:layout_constraintTop_toTopOf="#+id/itemSettingBg" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
Result is :
I need explain a little my codes for better understanding:
in CustomeBg there are some value:
companion object {
private val BODY_COLOR_RECT = Color.parseColor("#00534b")
private val LEFT_CIRCLE_COLOR = Color.parseColor("#ffffff")
private val RIGHT_CIRCLE_COLOR = Color.parseColor("#00534b")
private val RADIUS_LEFT = 100f
private val RADIUS_RIGHT = 100f
}
in this file in fact we have 3 object, two half-circle(one for left and one for right)
and we have one rectangular as our body, above code explain radius for every circle and their colors, you can see body color here too
Notice: you can change above value for your usage my freiend
At this class i draw simple ground for Tic-Tae-Toe. It consists of intercepted lines and "X" in the center of the cell.
So when User touches the cell, then textColor in it should be changed.
I use invalidate(rect) to redraw concrete cell, but in this case every cell changes it's textColor.
According to Romain Guy words, the canvas with whole view Rect comes
for drawing. The DisplayList will find interceptions between drawing commands and your dirty Rect, and only those commands will be drawn. But seems, that it doesn't work so way.
Partial invalidation in custom Android view with hardware acceleration
And also i found strange code change between 4.4 - 5.0 Android. So you can see, that mCurrentDirty disappeared from code at all.
Android View.invalidate(Rect) different behavior between two devices
P.S for SA this logic works correctly, and only dirty Rect is changed.
package com.eugeneshapovalov.fizmigclient
import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Rect
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import timber.log.Timber
class TicTacToeView : View, View.OnTouchListener {
constructor(context: Context) : this(context, null)
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
companion object {
const val CELL_SIZE_RATIO = 1 / 3f
const val LINE_SIZE = 2f
const val CELL_COUNT = 3
}
val linePaint = Paint()
val textPaint = Paint()
val dirtyCell = Rect()
var colorNumber: Int
init {
setOnTouchListener(this)
colorNumber = 0
linePaint.strokeWidth = resources.displayMetrics.density * LINE_SIZE
textPaint.textSize = 60f
}
private lateinit var cells: Array<Array<Rect>>
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
initCells()
}
private fun initCells() {
cells = Array(CELL_COUNT, { Array(CELL_COUNT, { Rect() }) })
val xCell = (width * CELL_SIZE_RATIO).toInt()
val yCell = (height * CELL_SIZE_RATIO).toInt()
for (i in 0 until CELL_COUNT) {
for (j in 0 until CELL_COUNT) {
cells[i][j].left = (x + j * xCell).toInt()
cells[i][j].top = (y + i * yCell).toInt()
cells[i][j].right = (x + (j + 1) * xCell).toInt()
cells[i][j].bottom = (y + (i + 1) * yCell).toInt()
}
}
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
drawLines(canvas)
drawText(canvas)
}
private fun drawLines(canvas: Canvas) {
// Vertical lines
canvas.drawLine(x + width * CELL_SIZE_RATIO, y, x + width * CELL_SIZE_RATIO, y + height, linePaint)
canvas.drawLine(x + width * 2 * CELL_SIZE_RATIO, y, x + width * 2 * CELL_SIZE_RATIO, y + height, linePaint)
// Horizontal lines
canvas.drawLine(x, y + height * CELL_SIZE_RATIO, x + width, y + height * CELL_SIZE_RATIO, linePaint)
canvas.drawLine(x, y + height * 2 * CELL_SIZE_RATIO, x + width, y + height * 2 * CELL_SIZE_RATIO, linePaint)
}
private fun drawText(canvas: Canvas) {
textPaint.color = when (colorNumber % 5) {
0 -> Color.BLACK
1 -> Color.BLUE
2 -> Color.RED
3 -> Color.GRAY
4 -> Color.YELLOW
else -> Color.GREEN
}
for (i in 0 until CELL_COUNT) {
for (j in 0 until CELL_COUNT) {
val rect = cells[i][j]
canvas.drawText("X", rect.exactCenterX(), rect.exactCenterY(), textPaint)
}
}
}
override fun onTouch(v: View?, event: MotionEvent?): Boolean {
when (event?.action) {
MotionEvent.ACTION_UP -> {
for (i in 0 until CELL_COUNT) {
for (j in 0 until CELL_COUNT) {
val rect = cells[i][j]
if (rect.contains(event.x.toInt(), event.y.toInt())) {
colorNumber += (j + 7)
Timber.d("Rect: ${rect.flattenToString()}")
invalidate(rect)
}
}
}
}
}
return true
}
}
I want to create a battery level indicator as in the image(which i circled). The green part should fill based on the available battery in the device.
Getting the battery percentage from the device like this
registerReceiver(mBatInfoReceiver, new IntentFilter(
Intent.ACTION_BATTERY_CHANGED));
So in the layout i am able to display the battery percentage.
public class BatteryIndicatorActivity extends Activity {
//Create Broadcast Receiver Object along with class definition
private BroadcastReceiver mBatInfoReceiver = new BroadcastReceiver() {
#Override
//When Event is published, onReceive method is called
public void onReceive(Context c, Intent i) {
//Get Battery %
int level = i.getIntExtra("level", 0);
TextView tv = (TextView) findViewById(R.id.textfield);
//Set TextView with text
tv.setText("Battery Level: " + Integer.toString(level) + "%");
}
};
But how to create a UI for this type of battery indicator. Is their any api to achieve this?, If not how to create such type of UI.
Your help will be appreciated.
Here is my CustomView for display battery level
class BatteryView #JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) :
View(context, attrs, defStyleAttr) {
private var radius: Float = 0f
private var isCharging: Boolean = false
// Top
private var topPaint =
PaintDrawable(Color.WHITE) // I only want to corner top-left and top-right so I use PaintDrawable instead of Paint
private var topRect = Rect()
private var topPaintWidthPercent = 50
private var topPaintHeightPercent = 8
// Border
private var borderPaint = Paint().apply {
color = Color.BLUE
style = Paint.Style.STROKE
}
private var borderRect = RectF()
private var borderStrokeWidthPercent = 8
private var borderStroke: Float = 0f
// Percent
private var percentPaint = Paint()
private var percentRect = RectF()
private var percentRectTopMin = 0f
private var percent: Int = 0
// Charging
private var chargingRect = RectF()
private var chargingBitmap: Bitmap? = null
init {
init(attrs)
chargingBitmap = getBitmap(R.drawable.ic_charging)
}
private fun init(attrs: AttributeSet?) {
val ta = context.obtainStyledAttributes(attrs, R.styleable.BatteryView)
try {
percent = ta.getInt(R.styleable.BatteryView_bv_percent, 0)
isCharging = ta.getBoolean(R.styleable.BatteryView_bv_charging, false)
} finally {
ta.recycle()
}
}
#SuppressLint("DrawAllocation")
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
val measureWidth = View.getDefaultSize(suggestedMinimumWidth, widthMeasureSpec)
val measureHeight = (measureWidth * 1.8f).toInt()
setMeasuredDimension(measureWidth, measureHeight)
radius = borderStroke / 2
borderStroke = (borderStrokeWidthPercent * measureWidth).toFloat() / 100
// Top
val topLeft = measureWidth * ((100 - topPaintWidthPercent) / 2) / 100
val topRight = measureWidth - topLeft
val topBottom = topPaintHeightPercent * measureHeight / 100
topRect = Rect(topLeft, 0, topRight, topBottom)
// Border
val borderLeft = borderStroke / 2
val borderTop = topBottom.toFloat() + borderStroke / 2
val borderRight = measureWidth - borderStroke / 2
val borderBottom = measureHeight - borderStroke / 2
borderRect = RectF(borderLeft, borderTop, borderRight, borderBottom)
// Progress
val progressLeft = borderStroke
percentRectTopMin = topBottom + borderStroke
val progressRight = measureWidth - borderStroke
val progressBottom = measureHeight - borderStroke
percentRect = RectF(progressLeft, percentRectTopMin, progressRight, progressBottom)
// Charging Image
val chargingLeft = borderStroke
var chargingTop = topBottom + borderStroke
val chargingRight = measureWidth - borderStroke
var chargingBottom = measureHeight - borderStroke
val diff = ((chargingBottom - chargingTop) - (chargingRight - chargingLeft))
chargingTop += diff / 2
chargingBottom -= diff / 2
chargingRect = RectF(chargingLeft, chargingTop, chargingRight, chargingBottom)
}
override fun onDraw(canvas: Canvas) {
drawTop(canvas)
drawBody(canvas)
if (!isCharging) {
drawProgress(canvas, percent)
} else {
drawCharging(canvas)
}
}
private fun drawTop(canvas: Canvas) {
topPaint.bounds = topRect
topPaint.setCornerRadii(floatArrayOf(radius, radius, radius, radius, 0f, 0f, 0f, 0f))
topPaint.draw(canvas)
}
private fun drawBody(canvas: Canvas) {
borderPaint.strokeWidth = borderStroke
canvas.drawRoundRect(borderRect, radius, radius, borderPaint)
}
private fun drawProgress(canvas: Canvas, percent: Int) {
percentPaint.color = getPercentColor(percent)
percentRect.top = percentRectTopMin + (percentRect.bottom - percentRectTopMin) * (100 - percent) / 100
canvas.drawRect(percentRect, percentPaint)
}
// todo change color
private fun getPercentColor(percent: Int): Int {
if (percent > 50) {
return Color.WHITE
}
if (percent > 30) {
return Color.YELLOW
}
return Color.RED
}
private fun drawCharging(canvas: Canvas) {
chargingBitmap?.let {
canvas.drawBitmap(it, null, chargingRect, null)
}
}
private fun getBitmap(drawableId: Int, desireWidth: Int? = null, desireHeight: Int? = null): Bitmap? {
val drawable = AppCompatResources.getDrawable(context, drawableId) ?: return null
val bitmap = Bitmap.createBitmap(
desireWidth ?: drawable.intrinsicWidth,
desireHeight ?: drawable.intrinsicHeight,
Bitmap.Config.ARGB_8888
)
val canvas = Canvas(bitmap)
drawable.setBounds(0, 0, canvas.width, canvas.height)
drawable.draw(canvas)
return bitmap
}
fun charge() {
isCharging = true
invalidate() // can improve by invalidate(Rect)
}
fun unCharge() {
isCharging = false
invalidate()
}
fun setPercent(percent: Int) {
if (percent > 100 || percent < 0) {
return
}
this.percent = percent
invalidate()
}
fun getPercent(): Int {
return percent
}
}
style.xml
<declare-styleable name="BatteryView">
<attr name="bv_charging" format="boolean" />
<attr name="bv_percent" format="integer" />
</declare-styleable>
drawable/ic_charging.xml
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="368.492"
android:viewportHeight="368.492">
<path
android:fillColor="#FFFFFF"
android:pathData="M297.51,150.349c-1.411,-2.146 -3.987,-3.197 -6.497,-2.633l-73.288,16.498L240.039,7.012c0.39,-2.792 -1.159,-5.498 -3.766,-6.554c-2.611,-1.069 -5.62,-0.216 -7.283,2.054L71.166,217.723c-1.489,2.035 -1.588,4.773 -0.246,6.911c1.339,2.132 3.825,3.237 6.332,2.774l79.594,-14.813l-23.257,148.799c-0.436,2.798 1.096,5.536 3.714,6.629c0.769,0.312 1.562,0.469 2.357,0.469c1.918,0 3.78,-0.901 4.966,-2.517l152.692,-208.621C298.843,155.279 298.916,152.496 297.51,150.349z" />
</vector>
Using
<package.BatteryView
android:id="#+id/battery_view"
android:layout_width="150dp"
android:layout_height="wrap_content"
android:src="#drawable/ic_charging"
app:bv_charging="false"
app:bv_percent="20" />
Github project
Hope it help
There are a lot of ways to do it. Here are few of them:
Use a ProgressBar, make it vertical, make it be drawn in the battery shape
Use a custom View, override onDraw() in it, and draw the battery shape on the canvas
Use a white image with a transparent battery shape. Place it over a view, where you fill a background vertically, or change background view's height.
I need to add Shimmer effect to image view as given in the link for my ImageView. The animation should be from bottom to top instead of left to right as in the sample picture. I have tried the facebook shimmer libraby but it supports from API 16 above only. I need to support if from 14 above. I have also tried this library but it doesn't have support for ImageViews as well as bottom to top animation. Is there any library to achieve the shimmer effect for an Imageview (with bottom to top animation)? or is there any way to implement this feature using ImageView?
You can create your own custom view!
class ShimmerView : View, ValueAnimator.AnimatorUpdateListener{
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() }
companion object {
const val EDGE_ALPHA = 12
const val SHADER_COLOR_R = 170
const val SHADER_COLOR_G = 170
const val SHADER_COLOR_B = 170
const val CENTER_ALPHA = 100
const val ITEM_BG_COLOR = Color.WHITE
val EDGE_COLOR = Color.argb(EDGE_ALPHA, SHADER_COLOR_R, SHADER_COLOR_G, SHADER_COLOR_B)
val CENTER_COLOR = Color.argb(CENTER_ALPHA, SHADER_COLOR_R, SHADER_COLOR_G, SHADER_COLOR_B)
const val LIST_ITEM_LINES = 3
const val CORNER_RADIUS = 2
const val LINE_HEIGHT = 15
const val H_SPACING = 12
const val W_SPACING = 16
const val IMAGE_SIZE = 50
const val ANIMATION_DURATION = 1500L
}
private var listItems: Bitmap? = null
private var animator: ValueAnimator? = null
private var paint: Paint? = null
private var shaderPaint: Paint? = null
private var shaderColors: IntArray? = null
private var lineHeight: Float = 0F
private var hSpacing: Float = 0F
private var wSpacing: Float = 0F
private var imageSize: Float = 0F
private var cornerRadius: Float = 0F
// 1. Инициализируем переменные.
// 1. Initialize variables.
fun init() {
val metric = context.resources.displayMetrics
cornerRadius = dpToPixels(metric, CORNER_RADIUS)
hSpacing = dpToPixels(metric, H_SPACING)
wSpacing = dpToPixels(metric, W_SPACING)
lineHeight = spToPixels(metric, LINE_HEIGHT)
imageSize = dpToPixels(metric, IMAGE_SIZE)
animator = ValueAnimator.ofFloat(-1F, 2F)
animator?.duration = ANIMATION_DURATION
animator?.interpolator = LinearInterpolator()
animator?.repeatCount = ValueAnimator.INFINITE
animator?.addUpdateListener(this)
paint = Paint()
shaderPaint = Paint()
shaderPaint?.isAntiAlias = true
shaderColors = intArrayOf(EDGE_COLOR, CENTER_COLOR, EDGE_COLOR)
}
// 2. Когда View отобразилась на экране, запускаем анимацию.
// 2. When View is displayed on the screen, run the animation.
override fun onVisibilityChanged(changedView: View?, visibility: Int) {
super.onVisibilityChanged(changedView, visibility)
when(visibility) {
VISIBLE -> animator?.start()
INVISIBLE, GONE -> animator?.cancel()
}
}
// 3. При выполнении анимации, изменяем положение шейдера и перерисовываем View.
// 3. When the animation, change the position of the shader and redraw View.
override fun onAnimationUpdate(valueAnimator: ValueAnimator) {
if(isAttachedToWindow) {
val factor: Float = valueAnimator.animatedValue as Float
updateShader(width = width.toFloat(), factor = factor)
invalidate()
}
}
// 4. Одновременно со стартом анимации, рисуем элементы.
// 4. Simultaneously with the start of the animation, draw the elements.
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
updateShader(width = w.toFloat())
if (h > 0 && w > 0) {
drawListItems(w, h)
} else {
listItems = null
animator?.cancel()
}
}
private fun updateShader(width: Float, factor: Float = -1F) {
val left = width * factor
val shader = LinearGradient(
left, 0F, left+width, 0F, shaderColors, floatArrayOf(0f, 0.5f, 1f), Shader.TileMode.CLAMP)
shaderPaint?.shader = shader
}
override fun onDraw(canvas: Canvas) {
canvas.drawColor(EDGE_COLOR)
canvas.drawRect(0F, 0F, canvas.width.toFloat(), canvas.height.toFloat(), shaderPaint)
if (listItems != null) { canvas.drawBitmap(listItems, 0F, 0F, paint) }
}
private fun drawListItems(w: Int, h: Int) {
listItems = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888)
val canvas = Canvas(listItems)
val item = getItemBitmap(w)
var top = 0
do {
canvas.drawBitmap(item, 0F, top.toFloat(), paint)
top += item.height
} while (top < canvas.height)
canvas.drawColor(ITEM_BG_COLOR, PorterDuff.Mode.SRC_IN)
}
private fun getItemBitmap(w: Int): Bitmap {
val h = calculateListItemHeight(LIST_ITEM_LINES)
val item = Bitmap.createBitmap(w, h, Bitmap.Config.ALPHA_8)
val canvas = Canvas(item)
canvas.drawColor(Color.argb(255, 0, 0, 0))
val itemPaint = Paint()
itemPaint.isAntiAlias = true
itemPaint.color = Color.argb(0, 0, 0, 0)
itemPaint.xfermode = PorterDuffXfermode(PorterDuff.Mode.DST_IN)
//Avatar
val rectF = RectF(wSpacing, hSpacing, wSpacing+imageSize, hSpacing+imageSize)
canvas.drawOval(rectF, itemPaint)
val textLeft = rectF.right + hSpacing
val textRight = canvas.width - wSpacing
//Title line
val titleWidth = (textRight - textLeft)*0.5F
rectF.set(textLeft, hSpacing, textLeft+titleWidth, hSpacing+lineHeight)
canvas.drawRoundRect(rectF, cornerRadius, cornerRadius, itemPaint)
//Time stamp
val timeWidth = (textRight - textLeft)*0.2F
rectF.set(textRight-timeWidth, hSpacing, textRight, hSpacing+lineHeight)
canvas.drawRoundRect(rectF, cornerRadius, cornerRadius, itemPaint)
//Text lines
for (i in 0..LIST_ITEM_LINES-1) {
val lineTop = rectF.bottom + hSpacing
rectF.set(textLeft, lineTop, textRight, lineTop+lineHeight)
canvas.drawRoundRect(rectF, cornerRadius, cornerRadius, itemPaint)
}
return item
}
private fun calculateListItemHeight(lines: Int): Int {
return ((lines*lineHeight) + (hSpacing*(lines+1))).toInt()
}
private fun dpToPixels(metrics: DisplayMetrics, dp: Int): Float {
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp.toFloat(), metrics)
}
private fun spToPixels(metrics: DisplayMetrics, sp: Int): Float {
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp.toFloat(), metrics)
}
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
animator?.removeAllUpdateListeners()
animator = null
listItems = null
}
}
Source
Facebook Shimmer Library does support API level 14. I am using it in a project with these settings:
defaultConfig {
....
minSdkVersion 14
targetSdkVersion 23
....
}
With this dependency:
dependencies {
....
compile 'com.facebook.shimmer:shimmer:0.1.0'
}
I am new to android and I am trying to draw a sine wave on the screen on the trigger of accelerometer values change. I just need a plain sine wave dynamically drawn on the screen(confined to screen). I can draw the coordinates on Canvas and validate screen dimensions but I am not able to think of how to convert sine values(ranging from 0 to 1) to the screen coordinates.
I was trying something like this in onSensorChanged():
tvarY = sin(tvarX)*2.0; // tvarX and tvarY are double values
tvarX= (tvarX+ 2); // 2.0 is for magnifying
xPosition = (float)tvarX;
yPosition = (float)tvarY;
But the values of tvarx using this approach are always switching between back and forth from infinity to 0. Can anybody please suggest me any approach to change the values and convert them into screen coordinates for drawing a proper sine wave?
Thanks :-)
I think you can use function:
path.rQuadTo(float dx1, float dy1, float dx2, float dy2)
Same as quadTo, but the coordinates are considered relative to the last point on this contour.
I wrote a sentence for your reference:
Path mpath = new Path();
mpath.moveTo(0, 100);
mpath.rQuadTo(20, 5, 40, 0);
mpath.rQuadTo(20, -5, 40, 0);
You can try once, then you will get a sine wave like this picture:
I think this method will be easier comparing with converting value of sin to coordinate.
Create custom view:
import android.animation.ValueAnimator
import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import android.view.View
class WaveView #JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
private var amplitude = 30f.toDp() // scale
private var speed = 0f
private val path = Path()
private var paint = Paint(Paint.ANTI_ALIAS_FLAG)
private var animator: ValueAnimator? = null
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
animator?.cancel()
animator = createAnimator().apply { start() }
}
override fun onDraw(c: Canvas) = c.drawPath(path, paint)
private fun createAnimator(): ValueAnimator {
return ValueAnimator.ofFloat(0f, Float.MAX_VALUE).apply {
repeatCount = ValueAnimator.INFINITE
addUpdateListener {
speed -= WAVE_SPEED
createPath()
invalidate()
}
}
}
private fun createPath() {
path.reset()
paint.color = Color.parseColor("#1da6f9")
path.moveTo(0f, height.toFloat())
path.lineTo(0f, amplitude)
var i = 0
while (i < width + 10) {
val wx = i.toFloat()
val wy = amplitude * 2 + amplitude * Math.sin((i + 10) * Math.PI / WAVE_AMOUNT_ON_SCREEN + speed).toFloat()
path.lineTo(wx, wy)
i += 10
}
path.lineTo(width.toFloat(), height.toFloat())
path.close()
}
override fun onDetachedFromWindow() {
animator?.cancel()
super.onDetachedFromWindow()
}
companion object {
const val WAVE_SPEED = 0.3f
const val WAVE_AMOUNT_ON_SCREEN = 350
}
private fun Float.toDp() = this * context.resources.displayMetrics.density
}
In activity layout(ConstraintLayout):
<com.uvn.test.WaveView
android:layout_width="0dp"
android:layout_height="350dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"/>