I'm trying to change the background color of a layout but depending on a value.
For example. 100 means read, 75: orange 60: yellow-ish and 20 like blue.
Also, have a transition between each color.
I'm new on Android development and I'm not sure exactly what to search for. If you can point me in the right direction, class, website or something.
update
I'm currently trying:
if(percentage <= 100 && percentage >= 85) {
// red
} else if(percentage < 85 && percentage >= 70) {
}
...
Try something like below -
In your Layout, assign an id to the View whose background you want to change, for e.g.
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/linear"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:background="#e5e5e5">
<!-- Other Views -->
</LinearLayout>
then in Activity -
LinearLayout ll = findViewById(R.id.linear);
if(percentage <= 100 && percentage >= 85) ll.setBackgroundColor(Color.RED);
else if(percentage < 85 && percentage >= 70) ll.setBackgroundColor(Color.BLUE);
I put together a demo. Assuming
amount is from 0 to 100
0 = rgb(0, 0, 255) - blue
50 = rgb(255, 255, 0) - yellow
100 = rgb(255, 0, 0) - red
This is a sample activity changing color every 2 seconds:
class BackgroundActivity : Activity() {
private lateinit var root: LinearLayout
private val values = arrayOf(0F, 40F, 60F, 100F, 10F)
private var counter = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_background)
root = findViewById(R.id.background)
val timer = Timer()
val task = object : TimerTask() {
override fun run() {
changeBackground(values[counter])
counter++
if (counter >= values.size)
timer.cancel()
}
}
timer.schedule(task, 0, 2000)
}
private fun changeBackground(amount: Float) {
val r = Math.min(255*amount*2/100, 255F)
val g = if (50 > amount) 255*amount*2/100 else 255*(100-amount)*2/100
val b = Math.max(255*(1-amount*2/100), 0F)
val color = getIntFromColor(r.toInt(), g.toInt(), b.toInt())
runOnUiThread {
root.setBackgroundColor(color)
}
}
private fun getIntFromColor(r: Int, g: Int, b: Int): Int {
val red = r shl 16 and 0x00FF0000 //Shift red 16-bits and mask out other stuff
val green = g shl 8 and 0x0000FF00 //Shift Green 8-bits and mask out other stuff
val blue = b and 0x000000FF //Mask out anything not blue.
return -0x1000000 or red or green or blue //0xFF000000 for 100% Alpha. Bitwise OR everything together.
}
}
The important part is the changeBackground() method - based on the amount calculates final color (int).
I am not really sure about the transition.
(thanks #initramfs for this answer)
I ended up using something else
boolean good = false;
double percentage = getPercentage((int) temp);
int screenHeight = getScreenHeight();
int pos1 = 0, pos2 = 0;
Integer[] firstColor = new Integer[]{0, 155, 255}, secondColor = new Integer[]{255, 152, 0};
if (percentage <= 0) {
firstColor = new Integer[]{0, 155, 255};
secondColor = new Integer[]{163, 235, 255};
pos1 = 0;
pos2 = 50;
good = true;
}
if (percentage <= 50 && !good) {
firstColor = new Integer[]{0, 155, 255};
secondColor = new Integer[]{163, 235, 255};
pos1 = 0;
pos2 = 50;
good = true;
}
if (percentage <= 100 && !good) {
firstColor = new Integer[]{163, 235, 255};
secondColor = new Integer[]{255, 152, 0};
pos1 = 50;
pos2 = 100;
}
int firstColor_X = screenHeight * (pos1 / 100);
int secondColor_X = (int) (screenHeight * ((double) pos2 / 100) - firstColor_X);
double slider_X = (screenHeight * (percentage / 100) - firstColor_X);
double ratio = slider_X / secondColor_X;
ArrayList<Integer> result = hexColor(secondColor, firstColor, ratio);
int red = result.get(0);
int green = result.get(1);
int blue = result.get(2);
background.setBackgroundColor(Color.rgb(red, green, blue));
Related
I have to create a HSV Histogram from a ARGB array using RenderScript in Android. This is the first time i am using RenderScript and i am not sure if i made a mistake because the performance is not so good. Creating a HSV histogram from an 1920x1080 bitmap takes between 100 and 150 ms.
The RenderScript code:
#pragma version(1)
#pragma rs java_package_name(com.test.renderscript)
#pragma rs_fp_relaxed
uchar3 bins;
rs_allocation histogramAllocation;
void __attribute__((kernel)) process(uchar4 in) {
float r = in.r / 255.0;
float g = in.g / 255.0;
float b = in.b / 255.0;
// convert rgb to hsv
float minRGB = min( r, min( g, b ) );
float maxRGB = max( r, max( g, b ) );
float deltaRGB = maxRGB - minRGB;
float h = 0.0;
float s = maxRGB == 0 ? 0 : (maxRGB - minRGB) / maxRGB;
float v = maxRGB;
if (deltaRGB != 0) {
if (r == maxRGB) {
h = (g - b) / deltaRGB;
}
else {
if (g == maxRGB) {
h = 2 + (b - r) / deltaRGB;
}
else {
h = 4 + (r - g) / deltaRGB;
}
}
h *= 60;
if (h < 0) { h += 360; }
if (h == 360) { h = 0; }
}
// quantize hsv
uint qh = h / (360.0 / bins.s0);
uint qs = (s * 100) / (101.0 / bins.s1);
uint qv = (v * 100) / (101.0 / bins.s2);
// calculate bin index and update the count at that index
// (v * bin size H * bin size S) + (s * bin size H) + h;
uint binIndex = (qv * bins.s0 * bins.s1) + (qs * bins.s0) + qh;
uint count = rsGetElementAt_uint(histogramAllocation, binIndex);
rsSetElementAt_uint(histogramAllocation, (count + 1), binIndex);
}
void init() {
uint histogramSize = bins.s0 * bins.s1 * bins.s2;
for (int i=0; i < histogramSize; i++) {
rsSetElementAt_uint(histogramAllocation, 0, i);
}
}
The Kotlin code:
class RsCreateHsvHistogram {
fun execute(rs: RenderScript, src: ByteArray, bins: HsvHistogram.Bins = HsvHistogram.Bins()): HsvHistogram {
val start = SystemClock.elapsedRealtimeNanos()
val histogramSize = bins.h * bins.s * bins.v
// create input allocation
val typeIn = Type.Builder(rs, Element.U8_4(rs))
.setX(src.size / 4)
.create()
val allocIn = Allocation.createTyped(rs, typeIn)
allocIn.copyFrom(src)
// create output allocation -> the histogram allocation
val typeOut = Type.Builder(rs, Element.I32(rs))
.setX(histogramSize)
.create()
val allocOut = Allocation.createTyped(rs, typeOut)
// run the render script
val script = ScriptC_create_hsv_histogram(rs)
script._bins = Short3(bins.h, bins.s, bins.v)
script._histogramAllocation = allocOut
script.forEach_process(allocIn)
// copy output allocation to histogram array
val histogramData = IntArray(histogramSize)
allocOut.copyTo(histogramData)
val stop = SystemClock.elapsedRealtimeNanos()
Timber.e("duration => ${(stop-start) / 1000000.0} ms")
return HsvHistogram(histogramData, bins)
}
}
I hope you can help me improve the performance. Do you think HSV histogram creation can be done in about 20ms? Is this realistic?
So I created a game where u can move your character and u need to pass lines with that character, but I have 1 problem. When the first line reaches bottom of my screen only then the score starts counting. I want to make so that when my character passes the lines gap the score would +1. What should I do?
EDIT: Also the score counts when last line reaches bottom!
if (obstacles.get(obstacles.size()-1).getRectangle().top >= Constants.SCREEN_HEIGHT ){
int xStart = (int) (Math.random()*(Constants.SCREEN_WIDTH- playerGap));
obstacles.add(0, new Obstacle(obstacleHeight, color, xStart, obstacles.get(0).getRectangle().top - obstacleHeight - obstacleGap, playerGap));
obstacles.remove(obstacles.size()-1);
score++;
}
}
public void draw(Canvas canvas){
for(Obstacle ob : obstacles)
ob.draw(canvas);
Paint paint = new Paint();
paint.setTextSize(100);
paint.setColor(Color.GREEN);
canvas.drawText("" + score, 50, 100 + paint.descent()- paint.descent(), paint);
EDIT:
int currY = -5*Constants.SCREEN_HEIGHT/4, rect;
while(currY < 0){
int xStart = (int) (Math.random()*(Constants.SCREEN_WIDTH- playerGap));
obstacles.add(new Obstacle(obstacleHeight, color, xStart, currY, playerGap));
currY += obstacleHeight + obstacleGap;
}
if (obstacles.get(obstacles.size() - 1).getRectangle().top >= currY) {
score++;
}
EDIT2:
private void populateObatacles(){
int currY = -5*Constants.SCREEN_HEIGHT/4;
while(currY < 0){
int xStart = (int) (Math.random()*(Constants.SCREEN_WIDTH- playerGap));
obstacles.add(new Obstacle(obstacleHeight, color, xStart, currY, playerGap));
currY += obstacleHeight + obstacleGap;
}
}
EDIT3:
if (obstacles.get(lastLineScored-1).getRectangle().top >= currY) {
score++;
lastLineScored--;
}
if (obstacles.get(obstacles.size()-1).getRectangle().top >= Constants.SCREEN_HEIGHT ){
int xStart = (int) (Math.random()*(Constants.SCREEN_WIDTH- playerGap));
obstacles.add(0, new Obstacle(obstacleHeight, color, xStart, obstacles.get(0).getRectangle().top - obstacleHeight - obstacleGap, playerGap));
obstacles.remove(obstacles.size()-1);
lastLineScored++;
}
}
}
public void draw(Canvas canvas){
for(Obstacle ob : obstacles)
ob.draw(canvas);
Paint paint = new Paint();
paint.setTextSize(100);
paint.setColor(Color.GREEN);
canvas.drawText("" + score , 50, 100 + paint.descent()- paint.descent (), paint);
EDIT4:
Obstacle manager http://pastebin.com/6E77QtHj
Obstacle http://pastebin.com/p33mPrat
Change your test. Instead of checking whether the top of the line is greater than SCREEN_HEIGHT, check whether it is greater than the character's Y coordinate. Also, you need to check against each
if (obstacles.get(obstacles.size()-1).getRectangle().top >= /** character Y coordinate */) {
score++;
}
if (obstacles.get(obstacles.size()-1).getRectangle().top >= Constants.SCREEN_HEIGHT ){
int xStart = (int) (Math.random()*(Constants.SCREEN_WIDTH- playerGap));
obstacles.add(0, new Obstacle(obstacleHeight, color, xStart, obstacles.get(0).getRectangle().top - obstacleHeight - obstacleGap, playerGap));
obstacles.remove(obstacles.size()-1);
}
EDIT: You need to keep track of which lines are already past the character and only up the score when a new line goes past. You'll need to keep an index of which lines have already been scored. So create a field to keep that index; let's call it lastScoredLine. It should be initialized to obstacles.size(). You'll have to adjust that field whenever a line falls off the screen.
if (obstacles.get(lastLineScored-1).getRectangle().top >= /** character Y coordinate */) {
score++;
lastLineScored--;
}
if (obstacles.get(obstacles.size()-1).getRectangle().top >= Constants.SCREEN_HEIGHT ){
int xStart = (int) (Math.random()*(Constants.SCREEN_WIDTH- playerGap));
obstacles.add(0, new Obstacle(obstacleHeight, color, xStart, obstacles.get(0).getRectangle().top - obstacleHeight - obstacleGap, playerGap));
obstacles.remove(obstacles.size()-1);
lastLineScored++;
}
I'm trying to set a custom value for the White Balance & temperature color in my camera app. I'm using camera2 API and I'm trying different ways to set this value. I found a method from a excel file to get the right RGB Temperature matrix [Red,Green,Blue] from the White Balance Value between 100 and 100.000.
I attached this method to a Seekbar and its working fine, my problem appear when I try to focus something white, then it becomes pink. Any kind of light looks like a pink torch in the screen.
I'm setting the values in this way:
mPreviewRequestBuilder.set(CaptureRequest.COLOR_CORRECTION_MODE, CaptureRequest.COLOR_CORRECTION_MODE_TRANSFORM_MATRIX);
RggbChannelVector rggb = getTemperatureVector(seekBackSelectedTemperature);
mPreviewRequestBuilder.set(CaptureRequest.COLOR_CORRECTION_GAINS, myRggbChannelVector);
In other way, my method to get the matrix is this one:
public static RggbChannelVector getTemperatureVector (int WhiteBalanceValue){
float InsertTemperature = WhiteBalanceValue;
float temperature = InsertTemperature / 100;
float red;
float green;
float blue;
//Calculate red
if (temperature <= 66)
red = 255;
else {
red = temperature - 60;
red = (float) (329.698727446 * (Math.pow((double) red, -0.1332047592)));
if (red < 0)
red = 0;
if (red > 255)
red = 255;
}
//Calculate green
if (temperature <= 66) {
green = temperature;
green = (float) (99.4708025861 * Math.log(green) - 161.1195681661);
if (green < 0)
green = 0;
if (green > 255)
green = 255;
} else
green = temperature - 60;
green = (float) (288.1221695283 * (Math.pow((double) red, -0.0755148492)));
if (green < 0)
green = 0;
if (green > 255)
green = 255;
//calculate blue
if (temperature >= 66)
blue = 255;
else if (temperature <= 19)
blue = 0;
else {
blue = temperature - 10;
blue = (float) (138.5177312231 * Math.log(blue) - 305.0447927307);
if (blue < 0)
blue = 0;
if (blue > 255)
blue = 255;
}
RggbChannelVector finalTemperatureValue = new RggbChannelVector(red/255,(green/255)/2,(green/255)/2,blue/255);
return finalTemperatureValue;
}
Maybe it's because the method of my CaptureRequest is not correct, but I don't find a way to fix it.
It worked after change the template to Still_capture or Manual Template and use the next method:
captureBuilder.set(CaptureRequest.CONTROL_AWB_MODE, CaptureRequest.CONTROL_AWB_MODE_OFF);
// adjust color correction using seekbar's params
captureBuilder.set(CaptureRequest.COLOR_CORRECTION_MODE, CaptureRequest.COLOR_CORRECTION_MODE_TRANSFORM_MATRIX);
captureBuilder.set(CaptureRequest.COLOR_CORRECTION_GAINS, CameraCapabilities.colorTemperature(Integer.parseInt(awbMode)));
public static RggbChannelVector colorTemperature(int whiteBalance) {
float temperature = whiteBalance / 100;
float red;
float green;
float blue;
//Calculate red
if (temperature <= 66)
red = 255;
else {
red = temperature - 60;
red = (float) (329.698727446 * (Math.pow((double) red, -0.1332047592)));
if (red < 0)
red = 0;
if (red > 255)
red = 255;
}
//Calculate green
if (temperature <= 66) {
green = temperature;
green = (float) (99.4708025861 * Math.log(green) - 161.1195681661);
if (green < 0)
green = 0;
if (green > 255)
green = 255;
} else {
green = temperature - 60;
green = (float) (288.1221695283 * (Math.pow((double) green, -0.0755148492)));
if (green < 0)
green = 0;
if (green > 255)
green = 255;
}
//calculate blue
if (temperature >= 66)
blue = 255;
else if (temperature <= 19)
blue = 0;
else {
blue = temperature - 10;
blue = (float) (138.5177312231 * Math.log(blue) - 305.0447927307);
if (blue < 0)
blue = 0;
if (blue > 255)
blue = 255;
}
Log.v(TAG, "red=" + red + ", green=" + green + ", blue=" + blue);
return new RggbChannelVector((red / 255) * 2, (green / 255), (green / 255), (blue / 255) * 2);
}
Another possible and more simpler solution; factor must be between 0 and 100:
private static RggbChannelVector computeTemperature(final int factor)
{
return new RggbChannelVector(0.635f + (0.0208333f * factor), 1.0f, 1.0f, 3.7420394f + (-0.0287829f * factor));
}
I want to save bitmap without transparent area.
Bitmap has large transparent pixel.
So i want to remove that
How can i do this?
I cant add picture so explain with symbols.
I dont want to crop function.
I hope use filter
┌────────────────────────┐
│ transparent area
│ ┌────────┐
│ crop this
└────────┘
└────────────────────────┘
To find the non-transparent area of your bitmap, iterate across the bitmap in x and y and find the min and max of the non-transparent region. Then crop the bitmap to those co-ordinates.
Bitmap CropBitmapTransparency(Bitmap sourceBitmap)
{
int minX = sourceBitmap.getWidth();
int minY = sourceBitmap.getHeight();
int maxX = -1;
int maxY = -1;
for(int y = 0; y < sourceBitmap.getHeight(); y++)
{
for(int x = 0; x < sourceBitmap.getWidth(); x++)
{
int alpha = (sourceBitmap.getPixel(x, y) >> 24) & 255;
if(alpha > 0) // pixel is not 100% transparent
{
if(x < minX)
minX = x;
if(x > maxX)
maxX = x;
if(y < minY)
minY = y;
if(y > maxY)
maxY = y;
}
}
}
if((maxX < minX) || (maxY < minY))
return null; // Bitmap is entirely transparent
// crop bitmap to non-transparent area and return:
return Bitmap.createBitmap(sourceBitmap, minX, minY, (maxX - minX) + 1, (maxY - minY) + 1);
}
Crop transparent border with this github.
public static Bitmap crop(Bitmap bitmap) {
int height = bitmap.getHeight();
int width = bitmap.getWidth();
int[] empty = new int[width];
int[] buffer = new int[width];
Arrays.fill(empty, 0);
int top = 0;
int left = 0;
int bottom = height;
int right = width;
for (int y = 0; y < height; y++) {
bitmap.getPixels(buffer, 0, width, 0, y, width, 1);
if (!Arrays.equals(empty, buffer)) {
top = y;
break;
}
}
for (int y = height - 1; y > top; y--) {
bitmap.getPixels(buffer, 0, width, 0, y, width, 1);
if (!Arrays.equals(empty, buffer)) {
bottom = y;
break;
}
}
empty = new int[height];
buffer = new int[height];
Arrays.fill(empty, 0);
for (int x = 0; x < width; x++) {
bitmap.getPixels(buffer, 0, 1, x, 0, 1, height);
if (!Arrays.equals(empty, buffer)) {
left = x;
break;
}
}
for (int x = width - 1; x > left; x--) {
bitmap.getPixels(buffer, 0, 1, x, 0, 1, height);
if (!Arrays.equals(empty, buffer)) {
right = x;
break;
}
}
return Bitmap.createBitmap(bitmap, left, top, right - left + 1, bottom - top + 1);
}
I took #Alvaro Menezes's answer and improved it as a Kotlin extension function. I tweaked it a bit, changed some variable names for better readability and it adds more fixes to the issue mentioned by #Ahamadullah Saikat that throws an IllegalArgumentException
Note that reading pixels by line improve a lot the performances against reading this independently as the accepted answer suggest.
/**
* Trims a bitmap borders of a given color.
*
*/
fun Bitmap.trim(#ColorInt color: Int = Color.TRANSPARENT): Bitmap {
var top = height
var bottom = 0
var right = width
var left = 0
var colored = IntArray(width, { color })
var buffer = IntArray(width)
for (y in bottom until top) {
getPixels(buffer, 0, width, 0, y, width, 1)
if (!Arrays.equals(colored, buffer)) {
bottom = y
break
}
}
for (y in top - 1 downTo bottom) {
getPixels(buffer, 0, width, 0, y, width, 1)
if (!Arrays.equals(colored, buffer)) {
top = y
break
}
}
val heightRemaining = top - bottom
colored = IntArray(heightRemaining, { color })
buffer = IntArray(heightRemaining)
for (x in left until right) {
getPixels(buffer, 0, 1, x, bottom, 1, heightRemaining)
if (!Arrays.equals(colored, buffer)) {
left = x
break
}
}
for (x in right - 1 downTo left) {
getPixels(buffer, 0, 1, x, bottom, 1, heightRemaining)
if (!Arrays.equals(colored, buffer)) {
right = x
break
}
}
return Bitmap.createBitmap(this, left, bottom, right - left, top - bottom)
}
Following the official doc:
The new bitmap may be the same object as source, or a copy may have been made.
You should take into account when you execute .recycle() with the source bitmap.
I am using GraphView Library for creating Bar graph as shown in picture, I need to reduce width of bars and increase space between bars.
Note- Red bars are currently what I'm getting and what I actually need is shown in black.
Below is the code snippet for the above graph:
GraphViewSeriesStyle barStyle = new GraphViewSeriesStyle();
barStyle.thickness = 5;
barStyle.setValueDependentColor(new ValueDependentColor() {
#Override
public int get(GraphViewDataInterface data) {
return Color.rgb(205, 0, 0);
}
});
// init example series data
GraphViewSeries scoreSeries = new GraphViewSeries(
"HealthCare Bar Graph", barStyle, new GraphViewData[] {
new GraphViewData(1, rsCVD),
new GraphViewData(2, rsHypertension4),
new GraphViewData(3, rsHypertension2),
new GraphViewData(4, rsHypertension1) });
GraphView graphView = new BarGraphView(this // context
, "GRAPH_VIEW_HEADING" // heading
);
graphView.setHorizontalLabels(new String[] { "1", "2",
"3", "4" });
graphView.addSeries(scoreSeries);
graphView.setViewPort(0, 25);
graphView.setScalable(true);
graphView.getGraphViewStyle().setHorizontalLabelsColor(Color.BLACK);
graphView.getGraphViewStyle().setVerticalLabelsColor(Color.BLACK);
graphView.getGraphViewStyle().setTextSize(16);
graphView.getGraphViewStyle().setGridColor(Color.WHITE);
// graphView.getGraphViewStyle().setLegendWidth(legendWidth)
int maxValue = myScore+1;
// search the interval between 2 vertical labels
double interval;
if (maxValue >= 0 && maxValue < 3) {
interval = 0.5; // increment of 1 between each label
} else if (maxValue >= 3 && maxValue < 55) {
interval = 5; // increment of 5 between each label
} else if (maxValue >= 55 && maxValue <= 110) {
interval = 10; // increment of 10 between each label
} else {
interval = 20; // increment of 20 between each label
}
// search the top value of our graph
int maxLabel = maxValue;
while (maxLabel % interval != 0) {
maxLabel++;
}
// set manual bounds
graphView.setManualYAxisBounds(maxLabel, 0);
// indicate number of vertical labels
int numVerticalLabels = (int) ((int) maxLabel / interval + 1);
Log.v(TAG, "numVerticalLabels: " + numVerticalLabels);
graphView.getGraphViewStyle().setNumVerticalLabels(numVerticalLabels);
graphView.getGraphViewStyle().setVerticalLabelsWidth(20);
// graphView.getGraphViewStyle().setLegendWidth(20);
graphView.getGraphViewStyle().setLegendSpacing(30);
// LinearLayout layout = (LinearLayout) findViewById(R.id.layout);
m_llayForRiskGraphContainer.addView(graphView);
there's currently no build in method to do this. but if you want to, you can easily modify the code. The point where you can add a spacing is here:
BarGraphView.java Line 95:
float left = (i * colwidth) + horstart -offset;
float top = (border - y) + graphheight;
float right = ((i * colwidth) + horstart) + (colwidth - 1) -offset;
canvas.drawRect(left, top, right, graphheight + border - 1, paint);
Just add a few pixels to left and remove a few pixels from right and it will look like you want.
https://github.com/jjoe64/GraphView/blob/master/src/main/java/com/jjoe64/graphview/BarGraphView.java#L97