In Android, I have a TextView and its content will be dynamic. I want to show a horizontal line after each line of text. I have searched a lot and found it for EditText( How to use ruled/horizontal lines to align text in EditText in Android?). I was planing to draw dynamic TextView and a horizontal line under them. But I don't know how can I detect an end of line. Any help will be highly appreciated. I want to have the same effect as the attached image of How to use ruled/horizontal lines to align text in EditText in Android?
I'm using the technique of drawing lines between each line of text in EditText and then I will make the EditText non-editable by setting setKeyListener(null) to the custom EditText object so that, the EditText acts like a TextView :)
A custom EditText that draws lines between each line of text that is displayed:
public class LinedEditText extends EditText {
private Rect mRect;
private Paint mPaint;
// we need this constructor for LayoutInflater
public LinedEditText(Context context, AttributeSet attrs) {
super(context, attrs);
mRect = new Rect();
mPaint = new Paint();
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(0x800000FF);
}
#Override
protected void onDraw(Canvas canvas) {
int count = getLineCount();
Rect r = mRect;
Paint paint = mPaint;
for (int i = 0; i < count; i++) {
int baseline = getLineBounds(i, r);
canvas.drawLine(r.left, baseline + 1, r.right, baseline + 1, paint);
}
super.onDraw(canvas);
}
}
Now use object of LinedEditText class where you need your TextView and make it non-editable.
An Example:
public class HorizontalLine extends Activity{
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setTitle("Android: Ruled/horizonal lines in Textview");
LinearLayout ll = new LinearLayout(this);
ll.setOrientation(LinearLayout.VERTICAL);
LayoutParams textViewLayoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
LinedEditText et = new LinedEditText(this, null);
et.setText("The name of our country is Bangladesh. I am proud of my country :)");
et.setLayoutParams(textViewLayoutParams);
et.setKeyListener(null);
ll.addView(et);
this.setContentView(ll);
}
}
et.setKeyListener(null) makes the EditText non-editable so, it acts like a TextView.
The Output:
Issue with cursor:
If you use et.setKeyListener(null) only then it is just not listening to keys but
user can see a cursor on the EditText. If you don't want this cursor just disable the EditText by adding this line:
et.setEnabled(false);
You can use a View for drawing line: For horizontal line:
<View
android:layout_height="1dp"
android:layout_width="fill_parent"
android:background="#ffffff" />
<TextView
android:layout_height="1dp"
android:layout_width="fill_parent"/>
Related
my code is here
textPaint.setStyle(Paint.Style.FILL);
textPaint.setTextAlign(Paint.Align.CENTER);
canvas.drawText(substring, textX, y, this.textPaint);
I want to add here also cursor like edittext
You can draw blinking cursor this way:
private long lastCursorChangeState = -1;
private boolean cursorVisible = true;
private Rect textBounds = new Rect();
#Override
protected void onDraw(Canvas canvas) {
if(isWriting){
if(System.currentTimeMillis() - lastCursorChangeState > 500) {
cursorVisible = !cursorVisible;
lastCursorChangeState = System.currentTimeMillis();
}
if(cursorVisible){
paint.getTextBounds(textToDraw, 0, textToDraw.length(), textBounds);
canvas.drawLine(textX+textBounds.right, textY-textSize, textX+textBounds.right, textY, paint);
}
postInvalidateDelayed(500);
}
}
No, as far as i know it's not possible, because the text is part of a bitmap and not interpreted as text by the textmarker interface.
I would suggest another solution:
Use a encapsulated layout-structure like that (layout only simplyfied!)
<FrameLayout>
<Canvas />
<TextView />
<!-- More textviews -->
</FrameLayout>
You can position the textview inside the framelayout by calling the function setX or setY. The textview will then always be drawn on top of the canvas, as long as the textview is defined after the canvas. Furthermore you can make it visible/invisible by code by calling the function setVisibility
In addition to that, you can add more Textviews to the framelayout dynamically using code instead of defining the textview statically in the layout xml
I've got a custom view that I'm trying to fill a GridLayout with. The custom view consists of a TextView inside of a circle. The problem I'm having is that the onDraw() method of my custom view never gets called so I always end up with a blank screen. When I populate my GridLayout with just regular TextViews it works just fine so I'm guessing the problem lies somewhere with my custom View.
My onCreate:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sample_collection);
gl = (GridLayout) findViewById(R.id.grid_sample_collection);
gl.setColumnCount(9);
gl.setRowCount(9);
LayoutInflater inflater = LayoutInflater.from(this);
for(int i=0;i<gl.getRowCount();i++){
for(int j=0;j<gl.getColumnCount();j++){
SampleCollectionView sampleCollectionView = new SampleCollectionView(this);
sampleCollectionView.setLabelText(i + "." + j);
gl.addView(sampleCollectionView);
//Adding the TextViews shown below works just fine
//TextView t = new TextView(this);
//t.setText(i + "." + j);
//t.setTextSize(30f);
//t.setPadding(30, 30, 30, 30);
//gl.addView(t);
}
}
int childCount = gl.getChildCount();
for (int i= 0; i < childCount; i++){
final SampleCollectionView sampleCollectionView = (SampleCollectionView) gl.getChildAt(i);
//final TextView text = (TextView) gl.getChildAt(i);
sampleCollectionView.setOnClickListener(new View.OnClickListener(){
public void onClick(View view){
Log.d("OnClickListener: ", "Clicked text: " + sampleCollectionView.getLabelText());
}
});
}
}
My custom view:
public SampleCollectionView(Context context){
super(context);
init();
}
public SampleCollectionView(Context context, AttributeSet attrs) {
super(context, attrs);
//get the attributes specified in attrs.xml using the name we included
TypedArray a = context.getTheme().obtainStyledAttributes(attrs,
R.styleable.SampleCollectionView, 0, 0);
try {
//get the text and colors specified using the names in attrs.xml
circleText = a.getString(R.styleable.SampleCollectionView_circleLabel); //0 is default
circleCol = a.getInteger(R.styleable.SampleCollectionView_circleColor, 0);
circleBorderCol = a.getInteger(R.styleable.SampleCollectionView_circleBorderColor, 0);
labelCol = a.getInteger(R.styleable.SampleCollectionView_labelColor, 0);
} finally {
a.recycle();
init();
}
}
public void init(){
mPaint = new Paint();
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int viewWidthHalf = this.getMeasuredWidth()/2;
int viewHeightHalf = this.getMeasuredHeight()/2;
int radius = 0;
if(viewWidthHalf>viewHeightHalf)
radius=viewHeightHalf-10;
else
radius=viewWidthHalf-10;
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mPaint.setAntiAlias(true);
mPaint.setColor(circleCol);
canvas.drawCircle(viewWidthHalf, viewHeightHalf, radius, mPaint);
mPaint.setTextAlign(Paint.Align.CENTER);
mPaint.setTextSize(20);
canvas.drawText(circleText, viewWidthHalf, viewHeightHalf, mPaint);
}
EDIT: After some testing I found out that the height and weight of my custom views were 0. After using setMinimumHeight and setMinimumWidth they are actually getting drawn and are responding to clicks. The only problem now is that for some reason, all of the custom views are completely invisible...
EDIT 2: Turns out the views weren't visible because I wasn't setting the View's properties like circleCol properly, which caused them to be set to their default value (0).
I managed to figure out what was going wrong. I created my custom Views without specifying a height/width. Naturally, this led to them getting a height and width of 0, with the GridLayout getting the same constraints as it was set to wrap_content.
After giving my views a proper height and width with the setMinimumHeight and setMinimumWidth methods I ran into another issue: The views weren't visible. They were being drawn as the onClickListener was responding to taps on various parts of the screen but I couldn't see them. The cause of this was the same thing that was responsible for the height and width being 0: The custom View's color properties weren't set properly. I was attempting to do this via XML but for some reason it couldn't retrieve the values of the properties I had specified in my XML file. Because the properties weren't specified, they reverted to their default values which resulted in no color being specified and no string getting supplied to the label. I fixed this by programmatically setting the properties.
I would like to add TextView and EditView into my custom LinearLayout programmatically.
But I don't know how.
Something like this (that doesn't work):
<com.custom.FavoritesViewer
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:visibility="gone"
android:id="#+id/favoritesViewer">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello"/>
</com.custom.FavoritesViewer>
and my custom layout
public class FavoritesViewer extends LinearLayout {
private Bitmap fullImage;
private int canvasWidth;
private int canvasHeight;
private final Paint paint = new Paint();
public FavoritesViewer(Context context, AttributeSet attrs) {
super(context, attrs);
setWillNotDraw(false);
initializeCanvasSize(context);
}
private void initializeCanvasSize(Context context) {
final Pair<Integer, Integer> screenSize = Utils.getScreenSize(context);
canvasWidth = screenSize.first;
canvasHeight = screenSize.second;
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(canvasWidth, canvasHeight / 3);
}
#Override
protected void onDraw(Canvas cvs) {
if (fullImage == null) {
fullImage = Bitmap.createBitmap(canvasWidth, canvasHeight / 3, Bitmap.Config.ARGB_8888);
final Canvas canvas = new Canvas(fullImage);
paint.reset();
paint.setColor(Color.parseColor("#AA000000"));
canvas.drawRect(0, 0, canvasWidth, canvasHeight / 3, paint);
}
cvs.drawBitmap(fullImage, 0, 0, null);
}
}
So I have a canvas (like background) and I would like to add some standart Views on top. I cannot add it on onDraw.
Is it any way to add View into custom Layout?
EDITTED
I need to implement some special UI with buttons. I want to wrap this in one component. I draw that UI on canvas and somehow should add buttons (It's enough for me to add simple ImageButton, not to draw an image and emulate button's behaviour). That's why I selected Layout as container and need to add Views programmatically.
As long as you call through to super (probably super.onDraw()), I imagine the parent class will draw the views you add as expected. It looks like you're just overriding onDraw, which would prevent the parent LinearLayout class from rendering it's content (like the TextView).
Try commenting out your onDraw method first, and see if the LinearLayout behaves as expected.
Also, what's the goal of the Custom Layout? There may be a better way to achieve your goal.
I have a customview looking like this:
public class CustomView extends View {
protected Context c;
protected String text;
... // and some more useful member variables...
public CustomView(String text, Context c, ...) {
this.text = text; this.c = c;
...
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
LinearLayout ll = new LinearLayout(c);
TextView tv = new TextView(c);
tv.setText(text);
ll.addView(tv);
ll.draw(canvas);
}
And in my main activity, I do this:
RelativeLayout gamelayout = (RelativeLayout) findViewById(R.id.gamelayout);
CustomView customview = new CustomView("Textview text", this);
gamelayout.addView(customview);
My problem is, that simply nothing is drawn, the drawn TextView does not appear in the "gamelayout". What am I doing wrong?
TextView objects are not able to draw directly to the canvas, as you've done you need to assign to a Layout and then do this:
ll.layout(0, 0, canvas.getWidth(), canvas.getHeight()); //Modify values as needed
Personally, I'm surprised there aren't errors thrown for calling ll.draw(). Unless you have a need to drawing a TextView I prefer drawing the text to the canvas:
canvas.drawText(...)
See documentation here
Your LinearLayout isn't attached to your view
try this.addView(ll) to add your LinearLayout to your view.
I've created a bar that is supposed to contain multiple views of different colors. A line has to be drawn that indicates the current position in the bar. Below is my simplified code:
public class Abar extends LinearLayout {
final Paint line_paint = new Paint();
private Context context;
public Abar(Context context) {
super(context);
this.context = context;
line_paint.setColor(Color.WHITE);
setWeightSum(2000);
setBackgroundResource(R.color.blue);
View view = new View(context);
view.setBackgroundResource(R.color.yellow);
this.addView(view, new LinearLayout.LayoutParams(0, LayoutParams.MATCH_PARENT, 1000));
}
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawLine(20, 80, 100, 80, line_paint);
}
}
Now, this does not seem to work. No matter if I swap the canvas.drawLine with super.onDraw, the line is not visible unless I remove the view.setBackgroundResource. How can I draw a line over the LinearLayout. I'd rather not use FrameLayout if possible.
Below are pictures of what I'm trying to achieve (adding a white line) on top of the bars (note: the white bar on the first picture is just exaggerated big for clearness on SO):
Override dispatchDraw() instead of onDraw(). You want to draw the line after the child views draw (which isn't in super.onDraw).