I am trying to change the default close button on the actionbar of the custom chrome tabs. I have tried to set using setCloseButtonIcon() However, the default close button still shows. I want to change the close to an arrow.
My code below:
public void openHomePage() {
final CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder();
builder.setToolbarColor(ContextCompat.getColor(getActivity(), R.color.primary));
final Bitmap backButton = BitmapFactory.decodeResource(getResources(), R.drawable.ic_arrow_back_black_48dp);
builder.setCloseButtonIcon(backButton);
builder.setShowTitle(true);
final CustomTabsIntent customTabsIntent = builder.build();
customTabsIntent.launchUrl(getActivity(), Uri.parse(mTvHomepage.getText().toString()));
}
I have an observation. Last month, when searching through SO for various chrome custom tab issues, I found this answer suggesting to use 24dp size icon and also found this question saying that it is working fine with PNG.
I have checked your code with using back arrow icon from here.
When I used "ic_arrow_back_black_48dp", it didn't change the default close button to an arrow (see left image).
final Bitmap backButton = BitmapFactory.decodeResource(getResources(), R.drawable.ic_arrow_back_black_48dp);
But when I used "ic_arrow_back_black_24dp", it perfectly changed the default close button to an arrow (see right image).
final Bitmap backButton = BitmapFactory.decodeResource(getResources(), R.drawable.ic_arrow_back_black_24dp);
As it has worked perfectly for me, you should also try with "24dp" size back arrow icon from here instead of "48dp" size back arrow icon.
Screenshot : [ Device: ASUS_Z00UD; OS: 6.0.1 ]
Presuming you are using the Google library and not on of the related ones the icons size should be 24dp as documented here.
This can be achieved with BitmapFactory Options:
BitmapFactory.Options options = new BitmapFactory.Options();
options.outWidth = 24;
options.outHeight = 24;
options.inScaled = true; //already default, just for illustration - ie scale to screen density (dp)
... = BitmapFactory.decodeResource(getResources(), R.drawable.ic_arrow_back_black_48dp, opts);
You can directly get BitmapDrawable from Drawable but not from VectorDrawable as setCloseButtonIcon requires #NonNull Bitmap icon
You can also use svg as follows. Download the svg from here ic_arrow_back_black_24px
Below methods are self explanatory:
private static Bitmap getBitmapFromDrawable(Context context, int drawableId) {
Drawable drawable = ContextCompat.getDrawable(context, drawableId);
if (drawable instanceof BitmapDrawable) {
return ((BitmapDrawable) drawable).getBitmap();
} else if (drawable instanceof VectorDrawable) {
return getBitmapFromVectorDrawable((VectorDrawable) drawable);
} else {
throw new IllegalArgumentException("Unable to convert to bitmap");
}
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
private static Bitmap getBitmapFromVectorDrawable(VectorDrawable vectorDrawable) {
Bitmap bitmap = Bitmap.createBitmap(vectorDrawable.getIntrinsicWidth(),
vectorDrawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
vectorDrawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
vectorDrawable.draw(canvas);
return bitmap;
}
You can use the above as:
builder.setCloseButtonIcon(getBitmapFromDrawable(this, R.drawable.ic_arrow_back_black_24px));
Ref from SO
why not you Add Image Asset and store in mipmap then it will be easier to use default icons inbuilt in android studio
Assest Studio
After Uploading You can easily access icons and image from mipmap in xml file using src resource in ImageView for an instant
android:src="#mipmap/ic_launcher"
To make this work in Kotlin (using Android KTX) with any 24dp drawable resource:
AppCompatResources.getDrawable(activity, R.drawable.ic_arrow_back_white_24dp)?.let {
builder.setCloseButtonIcon(it.toBitmap())
}
And if you need to do some tinting:
AppCompatResources.getDrawable(activity, R.drawable.ic_arrow_back_black_24dp)?.mutate()?.let {
DrawableCompat.setTint(it, Color.WHITE)
builder.setCloseButtonIcon(it.toBitmap())
}
If the drawable needs to be resized, then pass in the new width/height to the Drawable.toBitmap() function.
And if you are not using Kotlin then you can just use the equivalent of the Drawable.toBitmap() code:
fun Drawable.toBitmap(
#Px width: Int = intrinsicWidth,
#Px height: Int = intrinsicHeight,
config: Config? = null
): Bitmap {
if (this is BitmapDrawable) {
if (config == null || bitmap.config == config) {
// Fast-path to return original. Bitmap.createScaledBitmap will do this check, but it
// involves allocation and two jumps into native code so we perform the check ourselves.
if (width == intrinsicWidth && height == intrinsicHeight) {
return bitmap
}
return Bitmap.createScaledBitmap(bitmap, width, height, true)
}
}
val (oldLeft, oldTop, oldRight, oldBottom) = bounds
val bitmap = Bitmap.createBitmap(width, height, config ?: Config.ARGB_8888)
setBounds(0, 0, width, height)
draw(Canvas(bitmap))
setBounds(oldLeft, oldTop, oldRight, oldBottom)
return bitmap
}
For more see this answer.
I too had to face the same problem
Solution :-
1) Take image(back arrow) in png format.
2) Keep the image size under 24dp .
Although this question has already been answered, since I had this problem too and none of the above suggestions solved my Problem I want to share the way I solved my problem. Hope it helps others.
Ref
fun Context.openCustomTab(
url: String,
#ColorRes toolbarColor: Int,
#ColorRes iconColor: Int,
#DrawableRes drawable: Int
) {
CustomTabsIntent.Builder().let { ctb ->
AppCompatResources.getDrawable(this, drawable)?.let {
DrawableCompat.setTint(it, resources.getColor(iconColor))
ctb.setCloseButtonIcon(it.toBitmap())
}
ctb.setDefaultColorSchemeParams(
CustomTabColorSchemeParams.Builder()
.setToolbarColor(
ContextCompat.getColor(
this,
toolbarColor,
)
).build()
)
ctb.setShowTitle(true)
}.build().launchUrl(this, Uri.parse(url))
}
Related
I got a bitmap image, and application banners which need to be presentable.
I am using this function:
Bitmap.CreateScaledBitmap(src, width, height, bFilter);
It uses bFilter variable (true/false) to determine which filter to us, if it's true, it's bilinear filtering, otherwise Nearest-neighbor.
The problem is, that the results are the same, both are bad filters for me, see pic:
my pic: https://i.stack.imgur.com/i5hIZ.png
I need to downsize the original pictures, and that comes out with both filters, as you can see, it is far from ideal, and I need some help.
After my search, I could not find any solution that uses some other filter then those 2 previously mentioned, so my question is:
How to proccess the bitmap with another filter, for example Trilinear or Anisotropic filtering, or any other for that matter?
Here is my code:
private Bitmap GetBitmap(Drawable d, System.Drawing.Size? bounds)
{
int width = Math.Max(d.IntrinsicWidth, 1);
int height = Math.Max(d.IntrinsicHeight, 1);
if (bounds.HasValue)
{
width = Math.Min(width, bounds.Value.Width);
height = Math.Min(height, bounds.Value.Height);
}
var bd = d as BitmapDrawable;
if (bd != null)
{
Bitmap src = bd.Bitmap;
if (width == src.Width && height == src.Height)
{
return src;
}
else
{
var bFilter = true;
Android.Util.Log.Debug(TAG, "bFilter:" + bFilter); //<-----choose filter
return Bitmap.CreateScaledBitmap(src, width, height, bFilter);
}
}
Bitmap bitmap = Bitmap.CreateBitmap(width, height, Bitmap.Config.Argb8888);
Canvas canvas = new Canvas(bitmap);
d.SetBounds(0, 0, width, height);
d.Draw(canvas);
return bitmap;
}
[1]: https://i.stack.imgur.com/i5hIZ.png
I have a imported an SVG into my project as Vector Drawable.
I have my custom view
I know how to display a vector drawable using an ImageView by code (http://www.androidhive.info/2017/02/android-working-svg-vector-drawables/)
, which is described in several articles, but:
How can I draw my vectors using my custom view and by code with canvas ?
Is this possible to do. And if so, can someone give me a hint.
To do this you need to convert the vector to a bitmap as follows:
private fun getVectorBitmap(context: Context, drawableId: Int): Bitmap? {
var bitmap: Bitmap? = null
when (val drawable = ContextCompat.getDrawable(context, drawableId)) {
is BitmapDrawable -> {
bitmap = drawable.bitmap
}
is VectorDrawable -> {
bitmap = Bitmap.createBitmap(
drawable.intrinsicWidth,
drawable.intrinsicHeight, Bitmap.Config.ARGB_8888
)
val canvas = Canvas(bitmap)
drawable.setBounds(0, 0, canvas.width, canvas.height)
drawable.draw(canvas)
}
}
return bitmap
}
Then you can use the vector as a bitmap normally with your canvas:
canvas?.drawBitmap(getVectorBitmap(context, R.drawable.ic_icon), 500f, 500f, canvasPaint)
I'm going to turn #pskink's comment into an answer as I'm not so advanced in android as to quickly realize what he meant. I had to read #Krishna's answer to understand. So credits to both of them.
Here it goes:
A vector drawable inherits Drawable that can be drawn directly into a canvas. First, get the drawable from the resources using an android context:
val drawable = yourContext.getDrawable(drawableId)
Then simply draw into the canvas with it. The next line tells the drawable to draw on the top left corner, with a size of 32x32 pixels:
drawable.setBounds(0, 0, 32, 32)
Finally draw it into the canvas:
drawable.draw(canvas)
Is there some way to compare two Bitmaps which are wrapped by the BitmapDrawable.
The comparison should not fail if the sizes doesn't match, but it should match pixels and the color of the Bitmap
I am not sure how the native part of Android draws the Bitmap, because sameAs returns true even though the tint color is different.
If the size is different, I can create scaled Bitmap from the other and the compare these to.
In my source code I use DrawableCompat.setTint with the ImageViews Drawable and in the test code I load Drawable from resources and tint it same way.
Any ideas? I would like to have test which validates the source Drawable of ImageView and the color as well based on if it's pressed or not.
NOTE 1: My drawables are white, and I use tint to set color. Looping pixels for Bitmapsdoesn't work because they are white at that point, most likely Android native side uses tint color when drawing.
NOTE 2: Using compile 'com.android.support:palette-v7:21.0.0' and Palette.from(bitmap).generate(); doesn't help either because the returned palette has 0 swatches so can't get any color information there.
This is my current matcher:
public static Matcher<View> withDrawable(final Drawable d) {
return new BoundedMatcher<View, ImageView>(ImageView.class) {
#Override
public boolean matchesSafely(ImageView iv) {
if (d == null) {
return iv.getDrawable() == null;
} else if (iv.getDrawable() == null) {
return false;
}
if (d instanceof BitmapDrawable && iv.getDrawable() instanceof BitmapDrawable) {
BitmapDrawable d1 = (BitmapDrawable) d;
BitmapDrawable d2 = (BitmapDrawable) iv.getDrawable();
Bitmap b1 = d1.getBitmap();
Bitmap b2 = d2.getBitmap();
return b1.sameAs(b2);
}
return iv.getDrawable().getConstantState().equals(d.getConstantState());
}
#Override
public void describeTo(Description description) {
description.appendText("with drawable: ");
}
};
}
Thanks.
I use matchers written by Frankie Sardo for this purpose - https://gist.github.com/frankiesardo/7490059. There are a couple of matchers which must solve your issue. All credits to him.
I've had the opposite issue: matching a drawable when it's size and/or tint can change.
When I convert drawable to bitmap, I use it's default size and apply a black tint:
private fun getBitmapFromDrawable(drawable: Drawable): Bitmap {
val bitmap: Bitmap = Bitmap.createBitmap(
drawable.intrinsicWidth,
drawable.intrinsicHeight,
Bitmap.Config.ARGB_8888
)
val canvas = Canvas(bitmap)
drawable.apply {
setBounds(0, 0, canvas.width, canvas.height)
setTint(Color.BLACK)
draw(canvas)
}
return bitmap
}
Usage:
val bitmap = getBitmapFromDrawable(currentDrawable)
val otherBitmap = getBitmapFromDrawable(otherDrawable)
return bitmap.sameAs(otherBitmap)
You could use a new parameter for matching the drawable color...
private fun getBitmapFromDrawable(drawable: Drawable, color:Int): Bitmap {
val bitmap: Bitmap = Bitmap.createBitmap(
drawable.intrinsicWidth,
drawable.intrinsicHeight,
Bitmap.Config.ARGB_8888
)
val canvas = Canvas(bitmap)
drawable.apply {
setBounds(0, 0, canvas.width, canvas.height)
setTint(color)
draw(canvas)
}
return bitmap
}
Usage:
val bitmap = getBitmapFromDrawable(currentDrawable, color)
val otherBitmap = getBitmapFromDrawable(otherDrawable, color)
return bitmap.sameAs(otherBitmap)
How I can insert text inside Osmdroid marker? Suppose i need place several markers on map, and each marker should have number inside it. How I can make it? I try #setTitle() method, but it place text in balloon.
Update:
for(int i = 0; i < orders.size(); i++) {
mOrderPoint = orders.get(i).getStart();
final Order order = orders.get(i);
Marker orderMarker = new Marker(mMap);
orderMarker.setIcon(ContextCompat.getDrawable(mContext,R.drawable.order_pin));
orderMarker.setPosition(mOrderPoint);
}
This method takes a drawable from your resources, draws some text on top of it and returns the new drawable. All you need to do is give it the resource id of your bubble, and the text you want on top. Then you can pass the returned drawable wherever you want it.
public BitmapDrawable writeOnDrawable(int drawableId, String text){
Bitmap bm = BitmapFactory.decodeResource(getResources(), drawableId).copy(Bitmap.Config.ARGB_8888, true);
Paint paint = new Paint();
paint.setStyle(Style.FILL);
paint.setColor(Color.BLACK);
paint.setTextSize(20);
Canvas canvas = new Canvas(bm);
canvas.drawText(text, 0, bm.getHeight()/2, paint);
return new BitmapDrawable(bm);
}
Note:
To preserve density you need this constructor
BitmapDrawable (Resources res, Bitmap bitmap)
So, keeping your context, last return should be something like
return new BitmapDrawable(context.getResources(), bm);
This prevent an undesired resized drawable.
You want to look at osmdroid bonus pack.
Website: https://github.com/MKergall/osmbonuspack
There are many examples of stylized bubbles with text in it.
The answer by Blue_Alien does not work with vector drawables in Kotlin. The reason is due to BitmapFactory: it returns an error.
If you are programming in kotlin, there is a significantly simpler solution than using BitmapFactory.
Firstly, get the drawable:
val drawable = ContextCompat.getDrawable(context, R.drawable.order_pin)!!
Then simply use the toBitmap() function:
val bitmap = drawable.toBitmap()
And then the rest of the solution is effectively the same.
Final code:
val drawable = ContextCompat.getDrawable(context, R.drawable.order_pin)!!
val bitmap = drawable.toBitmap()
val paint = Paint();
paint.style = Style.FILL
paint.color = Color.BLACK
paint.textSize = 20
val canvas = Canvas(bitmap)
// Note, this code places the text in the center of the Bitmap (both vertically and horizontally)
// https://stackoverflow.com/a/11121873
canvas.drawText(text, bm.width / 2f,
bm.height / 2f - (paint.descent() + paint.ascent() / 2), paint)
val iconWithText = BitmapDrawable(context.resources, bitmap)
// Marker has to be initialised somewhere in your code
marker.icon = iconWithText
Of course, this code if for one time use. If you want to move it to a function, the parameter could either be the drawable or drawableID, and it would return the icon:
return BitmapDrawable(context.resources, bitmap)
I’m using thquinn’s DraggableGridView and load ~60 images into it. This all works fine. I had all the images needed in my assets, but want to create them at runtime since first of only the Text on the images change which seems redundant and I can reduce the appsize and the second reason is that I need to sometimes change the icons over the Air where adding wouldn’t be the problem but deleting from assets isn’t possible and would use unnecessary space. That briefly to explain my motives here.
So I’ve used the method from this Post to draw text over my Asset png and then convert it into a Bitmap to be able to use them in the LRUcache. This works with a few images but as soon as I try and display all needed Images I get an OutOfMemory error. The Base Images are 200x200 px which I think should also be scaled to the need size depending on screensize and density.
First of, this method doesn’t seem efficient because I create a Bitmap canvas then make a LayerdDrawable which I make into a Bitmap (for caching) again. Not sure, but it just feels like I’m creating to much temp images which clutter up the memory.
And then I’m using a BitmapDrawable which is depreciated. How would this method look without the BitmapDrawable??
Am I going about this the right way in general and How would I make this method efficiently so I don’t get the OOM error?!?
BTW. When I don’t use LRUcache and just return the LayerdDrawable for the GridView the images load fine but I get the Exception after a couple of Orientation changes!
This is the method as I have it atm:
private Bitmap createIcon(Drawable backgroundImage, String text,
int width, int height) {
String key = text.toLowerCase();
Bitmap cachedBitmap = getBitmapFromMemCache(key);
if (cachedBitmap != null){
Log.d("TRACE", "is cached");
return cachedBitmap;
}
else{
Bitmap canvasBitmap = Bitmap.createBitmap(width, height,
Bitmap.Config.ARGB_8888);
Canvas imageCanvas = new Canvas(canvasBitmap);
Typeface font = Typeface.createFromAsset(getActivity().getAssets(), "myriadpro.ttf");
Paint imagePaint = new Paint();
imagePaint.setTextAlign(Paint.Align.CENTER);
imagePaint.setTextSize(26);//
imagePaint.setTypeface(font);
imagePaint.setAntiAlias(true);
imagePaint.setColor(Color.parseColor("#562b12"));
backgroundImage.draw(imageCanvas);
imageCanvas.drawText(text, (width / 2)+4, (height / 2)-8, imagePaint);
LayerDrawable layerDrawable = new LayerDrawable(
new Drawable[]{backgroundImage, new BitmapDrawable(canvasBitmap)});
int w = layerDrawable.getIntrinsicWidth();
int h = layerDrawable.getIntrinsicHeight();
Bitmap bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
layerDrawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
layerDrawable.draw(canvas);
addBitmapToMemoryCache(key,bitmap);
return bitmap;
}
}
Update:
I have tried with another method now, which seems better because it’s not using BitmapDrawable. But I still get OOM error. Also it generally doesn’t seem to realy use the cached images, when I change orientation only 1 or 2 images come from the cache.
I also failed to metion before the this is inside a Fragment. Not sure if it matters. But in portrait mode i have only this Fragment and in Landscape there can be another one if the width allows it.
public Bitmap drawTextToBitmap(Context mContext, int resourceId, String mText) {
try {
int memory = (int) (Runtime.getRuntime().freeMemory() / 1024);
Log.d("TRACE", "memory " + memory);
Log.d("TRACE", mText);
String key = mText.toLowerCase();
Bitmap cachedBitmap = getBitmapFromMemCache(key);
if (cachedBitmap != null){
Log.d("TRACE", "is cached");
return cachedBitmap;
}
else{
Resources resources = mContext.getResources();
float scale = resources.getDisplayMetrics().density;
Bitmap bitmap = BitmapFactory.decodeResource(resources, resourceId);
android.graphics.Bitmap.Config bitmapConfig = bitmap.getConfig();
// set default bitmap config if none
if(bitmapConfig == null) {
bitmapConfig = android.graphics.Bitmap.Config.ARGB_8888;
}
bitmap = bitmap.copy(bitmapConfig, true); // OOE error happens here
Canvas canvas = new Canvas(bitmap);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(Color.rgb(110,110, 110));
paint.setTextSize((int) (25 * scale));
Rect bounds = new Rect();
paint.getTextBounds(mText, 0, mText.length(), bounds);
int x = (bitmap.getWidth() - bounds.width())/6;
int y = (bitmap.getHeight() + bounds.height())/5;
canvas.drawText(mText, x * scale, y * scale, paint);
addBitmapToMemoryCache(key,bitmap);
return bitmap;
}
} catch (Exception e) {
// TODO: handle exception
return null;
}
}
I worked on an app which needed to constantly hold 3-4 very large images in memory, and I was struggling with a problem very similar to yours. I loaded a bitmap from a byte array, then I needed to copy it to make it mutable, so that I could draw on it. This copy would cause there to be 1 too many bitmaps in memory, and then cause an OOM crash.
Eventually I found a way to load it once, and make it mutable at the same time:
Options options = new Options();
options.inMutable = true;
BitmapFactory.decodeResource(getResources(), id, options);
Use this, instead of copying the bitmap to make it mutable. I'm not sure if you really need the ARGB_8888 configuration, but if you don't, this should at least improve your memory efficiency.
Note: This will only work with Android 3.0 and above. For versions that need to run on 2.x and above, you can use a little reflection magic to see if the "inMutable" field exists in runtime. While this won't help on pre-3.0 devices, it will still provide a good improvement for most devices (and I've also noticed that devices running 2.x tend to have more memory flexibility).
Here's the code:
Options options = new Options();
options.inPurgeable = true;
options.inInputShareable = true;
Bitmap mutableBitmap = null;
try
{
Options.class.getField("inMutable").set(options, Boolean.TRUE);
Bitmap decodedBitmap = BitmapFactory.decodeResource(getResources(), id, options);
mutableBitmap = decodedBytes;
}
catch (NoSuchFieldException noFieldException)
{
Bitmap decodedBitmap = BitmapFactory.decodeResource(getResources(), id, options);
mutableBitmap = decodedBitmap .copy(decodedBitmap .getConfig(), true);
decodedBitmap .recycle();
}