I'm building a widget which displays some text. By widget I mean the kind which lies on the desktop.
The problem is that I want to change text's font at runtime. There is several textview I would like, at runtime, to set the first as bold, the second blue and italic for example, etc.
I came up with this :
TextView tv = new TextView(context);
tv.setText(stringToDisplay);
tv.setTextColor(0xa00050ff); // example
tv.setTextSize(30); // example
Bitmap b = loadBitmapFromView(tv);
updateViews.setImageViewBitmap(R.id.id_of_the_imageview, b);
with
private static Bitmap loadBitmapFromView(View v)
{
Bitmap b = Bitmap.createBitmap(v.getLayoutParams().width, v.getLayoutParams().height, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(b);
v.layout(0, 0, v.getLayoutParams().width, v.getLayoutParams().height);
v.draw(c);
return b;
}
but it wont work (NullPointerException on first line of loadBitmap), until I replace
v.getLayoutParams().width, v.getLayoutParams().height by fixed sizes like 250, 50
Bitmap b = Bitmap.createBitmap(250, 50, Bitmap.Config.ARGB_8888);
// ...
v.layout(0, 0, 250, 50);
But that's not a good solution ...
so I tried this :
LayoutInflater li = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View row = li.inflate(R.layout.widget_text, null);
TextView tv = (TextView) row.findViewById(R.id.id_of_the_textview);
widget_text being a layout similar to the displayed one but with TextViews instead of ImageViews, in the hope to get some size information out of it ..
but it's not working and I get this exception :
01-02 17:35:06.001: ERROR/AndroidRuntime(11025): Caused by: java.lang.IllegalArgumentException: width and height must be > 0
on the call to Bitmap.createBitmap()
so, someone could point me in the right direction?
Through RemoteViews you can still hide and show elements, so why not duplicate the text views for all font styles you might encounter?
something like:
widget.setViewVisibility(R.id.textview_italic,View.GONE);
widget.setViewVisibility(R.id.textview_bold,View.VISIBLE);
using images to display text should always be the last resort.
You can set the size dynamically:
float mySize = 10f;
updateViews.setFloat(R.id.textview, "setTextSize", mySize);
Related
I have a simple layout contains just one TextView.
I wanna load an image into TextView using ImageSpan.
Below method creates Spannable:
private Spannable getImageSpannable(int drawableId, int targetWidth, int targetHeight) {
Bitmap originalBitmap = BitmapFactory.decodeResource(getResources(), drawableId);
Bitmap bitmap = Bitmap.createScaledBitmap(originalBitmap, targetWidth, targetHeight, true);
Drawable dr = new BitmapDrawable(getResources(), bitmap);
dr.setBounds(0, 0, bitmap.getWidth(), bitmap.getHeight());
Spannable imageSpannable = new SpannableString("\uFFFC");
ImageSpan imgSpan = new ImageSpan(dr, DynamicDrawableSpan.ALIGN_BOTTOM);
imageSpannable.setSpan(imgSpan, 0, 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
return imageSpannable;
}
I use this method to create content like this:
public void setContent() {
SpannableStringBuilder content = new SpannableStringBuilder();
content.append(getImageSpannable(R.drawable.my_image, 100, 260));
content.append("\n");
txtContent.setText(content);
}
When I call setContent() method my result is something like this:
As you see there is small gap between ImageSpan and top of TextView.
This is not line spacing, because I set line spacing to 0.
And interesting point is when I remove "\n" from content(declared in setContent method) this space is gone.
And another point is that when I tried to measure content size using StaticLayout, with "\n" at the end bottom of line 0 it returns 270 and without "\n" it returns 260.
This behavior causes some difficulties for me, because I have to measure text and ImageSpan using StaticLayout and decide witch one can fit into TextView.
I appreciate everyone can help me.
Thanks.
Here's my xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="#+id/txtContent"
android:layout_width="300dp"
android:layout_height="500dp"
android:background="#fed9f4"
android:textSize="22sp"/>
</LinearLayout>
I'v done some tests and I find that font size is affects ImageSpan rendering.
Can somebody explain this affect please?
I hope this method works
The following line of code
public void setContent() {
SpannableStringBuilder content = new SpannableStringBuilder();
content.append(getImageSpannable(R.drawable.my_image, 100, 260));
content.append("\n");
txtContent.setText(content);
}
Change to
public void setContent() {
SpannableStringBuilder content = new SpannableStringBuilder();
content.append(getImageSpannable(R.drawable.my_image, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
content.append("\n");
txtContent.setText(content);
}
And resize "R.drawable.my_image" dimensions
I have created a drag and drop activity in my app for reordering items using the code found here.
one of the activities involves reordering images, so I added an image view to the layout and adjusted the code to add the right images and all that stuff. Everything works fine except that when grabbing items, the first item you grab from each slot is always what shows up whenever you grab any other item from that slot.
So for example, say i have items a, b, c, and d, if I grab item a and move it to the end, b is now in that first slot. if I grab item b now, the floating view that gets dragged around is still showing a. When it's dropped, everything looks fine and is in the new order that it is supposed to be in. Now in that same example, if I take the second item, which is now item c, and move it anywhere, the same thing happens. Dragging the first item always shows a and the second always shows c.
I can also take item a, move it to the second, then third, then fourth etc, slot and then no matter which item I grab, item a is always the one showing when an item is being dragged.
I'm confused as to what is causing this, i think it may be something to do with this section of code, specifically with the drawing Cache.
private void startDrag(int itemIndex, int y) {
stopDrag(itemIndex);
Log.i("startdrag","view " + itemIndex);
View item = getChildAt(itemIndex);
if (item == null) return;
item.setDrawingCacheEnabled(true);
if (mDragListener != null)
mDragListener.onStartDrag(item);
// Create a copy of the drawing cache so that it does not get recycled
// by the framework when the list tries to clean up memory
Bitmap bitmap = Bitmap.createBitmap(item.getDrawingCache());
WindowManager.LayoutParams mWindowParams = new WindowManager.LayoutParams();
mWindowParams.gravity = Gravity.TOP;
mWindowParams.x = 0;
mWindowParams.y = y - mDragPointOffset;
mWindowParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
mWindowParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
mWindowParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
| WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
| WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
mWindowParams.format = PixelFormat.TRANSLUCENT;
mWindowParams.windowAnimations = 0;
Context context = getContext();
ImageView v = new ImageView(context);
v.setImageBitmap(bitmap);
WindowManager mWindowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
mWindowManager.addView(v, mWindowParams);
mDragView = v;
}
The original app also occasionally shows the wrong item when dragging, but I cannot get it to repeat reliably, which is why I think it has something to do with the drawing cache. My version has images and its probably trying to save memory by reusing the old views. I really don't know, if there's more I need to show just ask.
Thanks in advance.
Rather than using the drawing cache, try instead to get the bitmap from the view directly. This is how Google does it in their DynamicListView as you can see below. I've been using a lightly modified version of it without issue. You can find more info here : https://www.youtube.com/watch?v=_BZIvjMgH-Q
/** Draws a white border over the screenshot of the view passed in. */
private Bitmap getBitmapWithBorder(View v) {
Bitmap bitmap = getBitmapFromView(v);
Canvas can = new Canvas(bitmap);
Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
Paint paint = new Paint();
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(LINE_THICKNESS);
paint.setColor(getResources().getColor(R.color.transparent_white));
can.drawBitmap(bitmap, 0, 0, null);
can.drawRect(rect, paint);
return bitmap;
}
/** Returns a bitmap showing a screenshot of the view passed in. */
private Bitmap getBitmapFromView(View v) {
Bitmap bitmap = Bitmap.createBitmap(v.getWidth(), v.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas (bitmap);
v.draw(canvas);
return bitmap;
}
I'm trying to use a custom view as the icon next to the up button. I searched for a solution online and read something about using a drawing cache and tried implementing it in my code but no icon is showing.
ActionBar bar = getSupportActionBar();
bar.setBackgroundDrawable(new ColorDrawable(getResources().getColor(R.color.scheme_dark_blue)));
bar.setTitle(i.getStringExtra("schoolDay"));
CalendarIcon icon = (CalendarIcon) getLayoutInflater().inflate(R.layout.icon_calendar, null);
icon.setDrawingCacheEnabled(true);
day = Integer.parseInt(i.getStringExtra("date"));
icon.setData(day + "");
icon.buildDrawingCache();
bar.setIcon(new BitmapDrawable(getResources(), icon.getDrawingCache()));
bar.setDisplayHomeAsUpEnabled(true);
Thanks for any help :)
Your code is probably not working because the view does not have a size yet. I also suggest you don't use the drawing cache but instead draw the view on a canvas.
I have written a little example where the view is given a size when it doesn't have one yet. You may want to tune to code a bit to fit your needs.
Example:
public static Drawable createDrawableFromView(View v, int requestedWidth, int requestedHeight) {
// Because the view is never shown it does not have a size, but if shown don't resize it.
if (v.getMeasuredHeight() <= 0) {
// You may want to change these lines according to the behavior you need.
int specWidth = MeasureSpec.makeMeasureSpec(requestedWidth, MeasureSpec.AT_MOST);
int specHeight = MeasureSpec.makeMeasureSpec(requestedHeight, MeasureSpec.AT_MOST);
v.measure(specWidth, specHeight);
}
Bitmap b = Bitmap.createBitmap(v.getMeasuredWidth(), v.getMeasuredHeight(), Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(b);
v.layout(0, 0, v.getMeasuredWidth(), v.getMeasuredHeight());
v.draw(c);
return new BitmapDrawable(v.getContext().getResources(), b);
}
Usage:
getActionBar().setTitle("View to icon test");
TextView view = new TextView(this);
view.setText("AI");
view.setGravity(Gravity.CENTER);
view.setTextColor(Color.RED);
getActionBar().setIcon(createDrawableFromView(view, 250, 250));
Result:
I'm now trying to resolve an issue with somehow overlapping text in TextView.
I've posted a question about that , but I've also tried to solve it myself. I decided to count the amount of text which causes the textview to break the line. I came up with this unpolished code, which basically should inflate the view, set it's layout params according to the displayed size and then run onmeasure and return lineCount. I plan than to use probably binary search to find exact text length which fits into the textview, but even before I've just tried to run the code and see how it behaves. It's kind of weird, because it gives me different results, than what I see on screen than. I even tried to alter the textsize according to scaled density, because I wasn't sure whether it's been taken into account.
Here is the code. It returns three,but when I render the layout to screen the text takes up only two lines.
public int getCountOfLines(Context context, int widgetDp){
LayoutInflater mLayoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
LinearLayout layout = (LinearLayout) mLayoutInflater.inflate(R.layout.event_basic_large ,null, false);
TextView titleTextView = (TextView) layout.findViewById(R.id.itemTitle);
titleTextView.setText(TEST_TEXT);
float density = context.getResources().getDisplayMetrics().density;
float textDensity = context.getResources().getDisplayMetrics().scaledDensity;
titleTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, titleTextView.getTextSize()/density*textDensity);
layout.setLayoutParams(
new LinearLayout.LayoutParams(
(int) (widgetDp*density),
ViewGroup.LayoutParams.WRAP_CONTENT)
);
layout.measure(View.MeasureSpec.makeMeasureSpec(
(int) (widgetDp*density), View.MeasureSpec.EXACTLY),
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
);
Log.d(TAG, titleTextView.getLineCount() + " lines, width "+titleTextView.getMeasuredWidth());
return titleTextView.getLineCount();
}
I ran into this a while back and after searching and trying finally got the following function to work:
public int getLineCount(String testString, float textSize,
float width) {
Rect bounds = new Rect();
Paint paint = new Paint();
paint.setTypeface(<font>);//set font that you are using for this
paint.setTextSize(textSize);
paint.getTextBounds(testString, 0, testString.length(), bounds);
return (int) Math.ceil(bounds.width() / width);
}
This function uses textSize and width to give number of lines.
This functions give number of line in a textview before it is displayed.
In my app I need to know what is the size of main container of activity. I have written following code:
Button btnActionMenu = (Button) findViewById(R.id.actionbar_menu);
btnActionMenu.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
int width = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 40, getResources().getDisplayMetrics());
RelativeLayout rlContainer = (RelativeLayout) findViewById(R.id.inner_content);
Bitmap bitmap = Bitmap.createBitmap(rlContainer.getLayoutParams().width, rlContainer.getLayoutParams().height, Config.ARGB_8888);//
Canvas canvas = new Canvas(bitmap);
rlContainer.draw(canvas);
SlideoutHelper.setBitmap(bitmap, width);
startActivity(new Intent(Home.this, MenuActivity.class));
overridePendingTransition(0, 0);
}
});
rlContainer is the root container of activity. When I run and click on button, the application crashes and Logcat says "Application Null pointer exception" and points to line
Bitmap bitmap = Bitmap.createBitmap(rlContainer.getLayoutParams().width, rlContainer.getLayoutParams().height, Config.ARGB_8888);
any suggestion would be appreciated. Thanks
try this :
v.measure(
MeasureSpec.makeMeasureSpec(v.getLayoutParams().width,MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(v.getLayoutParams().height, MeasureSpec.EXACTLY));
v.layout(0, 0, v.getMeasuredWidth(), v.getMeasuredHeight());
Bitmap bitmap = Bitmap.createBitmap(v.getWidth(), v.getHeight()
,Bitmap.Config.RGB_565);
have you tried getMeasuredHeight and getMeasuredWidth , or getHeight and getWidth (on the rlContainer variable ) ?
note that this works only after the view was prepared , and not inside the onCreate() method . however , it will work on this situation since it's inside a user-related action , which occurs after the views were shown to the user.
btw , about your code , "rlContainer.getLayoutParams().width" might return a negative value , in case it's wrap-content or match-parent .