I wrote a custom text view(it doesn't matter what kind of view actually) that extends from View. I added padding to this view in my XML document and read these padding and pass it to super to apply these padding. And in my onDraw and onMeasure I also took these padding into consideration and everything works just fine.
EXCEPT, if I scroll this view via the method View.scrollTo(), the padding no longer works. By saying no longer works, I mean the content that drawn on canvas doesn't respect the view's padding, like the images shown below:
I want to know if there's any workaround on this?(PS: don't tell me to use TextView instead of making my own. I'm doing this for some reason, the only problem I want to solve here is just the padding stuff, not some brilliant alternatives, thanks!)
EDIT:
my xml
<com.example.custom.MyTextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/text_area"
android:layout_width="100dp"
android:layout_height="100dp"
android:background="#drawable/text_bg"
android:padding="10dp"/>
and in my constructor:
public MyTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
// by calling super, the super class will take care of the paddings
// internal, and after this, I just have to get the paddings by
// getPaddingTop(),getPaddingLeft(),getPeddingRight(),getPaddingBottom() etc.
}
and in my onMeasure:
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
......
setMeasuredDimension(getPaddingLeft()+contentWidth+getPaddingRight()
,getPaddingTop()+contentHeight+getPaddingBottom());
//whereas the contentWidth and contentHeight is determined by the widthMeasureSpec
// and heightMeasureSpec and some certain logic inside the view.
// I don't think this will cause the view's content to exceed the conten area
}
and in my onDraw:
public void onDraw(Canvas canvas){
Path path = new Path();
for(String eachLine: text){
path.moveTo(startX,startY);
path.lineTo(endX,endY);
canvas.drawTextOnPath(eachLine,path,0,0,painter);
...
}
....
//the startX,startY,endX, endY has already took the padding into consideration.
//*NOTE: The only solution that I can think of is that I control this
//drawing logic according to the padding. But this approach still won't fix the
//problem, for example: what if I scroll half line height? How do I draw the
//half of the line? So there must be some other approach that I don't know.
}
Related
Im trying to create a sort of HUD overlay for Google Cardboard.
The HUD needs to be duplicated (one for each eye). A simplistic solution would be to manually copy all the XML elements into another view but giving them different names. This feels like a bad approach since it involves lots of code duplication.
So i came up with the following solution for a ViewGroup with is supposed to render everything two times:
public class StereoView extends FrameLayout {
private static final String TAG = StereoView.class.getSimpleName();
public StereoView(Context context) {
super(context);
init(context);
}
private void init(Context context) {
testPaint.setColor(Color.RED);
}
private Paint testPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right/2, bottom);
}
#Override
protected void dispatchDraw(Canvas canvas) {
canvas.save();
canvas.translate(getWidth() / 2, 0);
super.dispatchDraw(canvas);
canvas.restore();
super.dispatchDraw(canvas);
}
public StereoView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public StereoView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public StereoView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(context);
}
}
The first problem is that neither dispatchDraw or onDraw is called except from one or two times. It is not called when child views are invalidated.
The second problem is that background on elements which has a with of MATCH_PARENT renders outside the ViewGroups inner bounds:
200DP width
MATCH_PARENT
Is this approach hoping for too much, or am i thinking wrong? Creating a completely custom view to handle complex layouts and images seems like lots of work while copying my layout seems like bad design.
You say:
A simplistic solution would be to manually copy all the XML elements
into another view but giving them different names. This feels like a
bad approach since it involves lots of code duplication.
Actually you can go ahead and use the <include> tag. All you need to do is create a layout that contains all the views that you are going to show to a single eye. Then in your main layout you have to <include> this layout twice, one for the left eye and the other for the right eye.
You might wonder, if this is the case then how can i use findViewById() on this main layout, since now there will be two views with the same id. Well, you can fix that by doing it as follows. Let's say you have created the eye.xml layout. Then your main_layout should look like below.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<include
android:id="#+id/leftEye"
layout="#layout/eye" />
<include
android:id="#+id/rightEye"
layout="#layout/eye" />
</LinearLayout>
When you do the findViewById() in your code, you could do that as follows:
RelativeLayout leftEye = (RelativeLayout)findViewById(R.id.leftEye);
ImageView iv = (ImageView)leftEye.findViewById(R.id.something);
You need write a simple method in your activity where you just pass the leftEye or rightEye as a parameter and perform all code in this method. This lets you perform UI changes in leftEye and rightEye simultaneously.
In the future, you could write a custom View in which you could just inflate the eye.xml. That would modularize your idea.
This is my thoughts to your problem.
A ViewGroup hosts Views; any xml layout are Views, so extend a ViewGroup of your choice, either LinearLayout ,Framelayout-(i prefer), and in your initialisation process, inflate your Layout twice and add them as Views later you can research on how to use onLayout() to position your Views in your preferred Location.
And what ever you call a View 1, View 2 needs to be onboard, you can bind the two, using any approach you want, interfaces or beans
Note
you create one layout and inflate it twice. which will give you two separate View objects, hence this won't be code duplication as its more of
Elltz _20yearElltz = new Elltz(20),_21yearElltz = new Elltz(21);
Hope it helps
I have one imageView and I am trying to give it minus margin top as much as its height / 2. I can do it at programmatically but i wondred is it possible at xml also andorid published percentrelative layout . I don't know how to to do it or possible?
--Edit: As #aga suggests, there seems to be a way to achieve it via the Percent Support Library--
If you want to use this type of imageView more often throughout your application you could extend imageview and put your margin-code inside of it's onMeasure:
public class HalfMarginImageView extends ImageView {
public HalfMarginImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
((ViewGroup.MarginLayoutParams) getLayoutParams()).topMargin = -getMeasuredHeight() / 2;
}
}
for this to work the view must be part of a ViewGroup.Also make sure you use the constructor with AttributeSet, oltherwise you can't create the View from xml. Yo then just include a CustomView in your layout xml, select HalfMarginImageView and use it as normal imageView.
I am using a custom View and plotting line of double size of Custom View
Following is my Custom View in xml
<HorizontalScrollView
android:layout_width="780dp"
android:layout_height="300dp"
android:layout_x="20dp"
android:layout_y="180dp">
<com.john.TestApp.ScrollLineView
android:id="#+id/RectroGraphView"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
>
</com.john.TestApp.ScrollLineView> </HorizontalScrollView>
And this is my ScrollLineView view class code. As you can see my line is more than double size of custom view. but when I am running it is not even plotting any line.
I want whenever my line goes out of size, custom view has to be scrollable.
public ScrollLineView(Context context, AttributeSet attrs)
{
super(context, attrs);
pTestPaint.setColor(Color.RED);
setHorizontalScrollBarEnabled(true);
setWillNotDraw(false);
}
#Override
protected void onDraw(Canvas StoredPlotCanvas)
{
super.onDraw(StoredPlotCanvas);
StoredPlotCanvas.drawLine(0, 150, 2500, 150, pTestPaint);
StoredPlotCanvas.drawLine(0, 200, 1000, 200, pTestPaint);
}
Any help?
It can be done this way, but you would need to override and implement custom onMeasure method in your ScrollLineView class. Then whenever you draw the line you would need to call requestLayout() and adjust view size in onMeasure method, so it can be scrolled.
In my opinion it would be much better solution to implement custom scrolling in ScrollLineView. You would just need to adjust offsets of what you are currently drawing.
How to set button height less then 30px? I tried the next:
setHeight(30); (set 150 is working, but set 30 is not)
setPadding(0,-20,0,-20); (width is changes, height is not)
LinearLayout.LayoutParams (set 150 is working, but set 30 is not)
new Button(this, null, android.R.attr.buttonStyleSmall); - effects only on button text
Button mainButton = new Button(this);
mainButton.setPadding(0, 0, 0, 0);
mainButton.setLayoutParams(new LayoutParams(FlowLayout.LayoutParams.WRAP_CONTENT,FlowLayout.LayoutParams.WRAP_CONTENT));
l.addView(mainButton);
SmallButton mainButton = new SmallButton(this);
mainButton.setText(s);
mainButton.setBackgroundResource(R.drawable.button_drawable);
mainButton.setLayoutParams(new LayoutParams(FlowLayout.LayoutParams.WRAP_CONTENT,FlowLayout.LayoutParams.WRAP_CONTENT));
l.addView(mainButton);
public class SmallButton extends Button {
public SmallButton(Context context) {
super(context);
}
public SmallButton(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SmallButton(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec,MeasureSpec.makeMeasureSpec(MeasureSpec.EXACTLY, 35));
}
#Override
protected void onDraw(Canvas canvas) {
canvas.save();
canvas.translate(0,-5);
super.onDraw(canvas);
canvas.restore();
}
}
I have a Shape drawable with gradient for my background as I don't know the exact width. I also tried to setup its height. Is it possible to make button wrap it text only?
EDIT:
The problem is solved with the use of TextView instead of the Button. Ofcourse if you are not using the specific/overriden things in Button.class.
There are two parameters.
Button.setMinimumHeight() and Button.setMinHeight(). Ensure that both of them are set to value "0".
Every View in Android can have a minimum width and height set for them. A Button will set these depending on the style it is using on the device it is using.
However, this can be overridden in the xml for the button:
android:minHeight="0dip"
android:minWidth="0dip"
As #Sudar Nimalan points out, this is related to the background of the button. If you don't wish to create your own button class, you can simply set the background to something smaller. Try setting the background to a solid colour, or create your own background for it.
try this
mainButton.setIncludeFontPadding(false);
I think, This causes due the default background drawable minimum height size, you can sent a empty background or color and test these scenarios.
If you want to change the height with default drawable, you can create your Custom Button extending the Button class and override on onMeasure and onDraw etc.
override the onDraw to shift the default button drawing:
onDraw(canvas){
canvas.save();
canvas.translate(0,value to shift);
super.onDraw(canves);
canvas.restore();
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec,MeasureSpec.makeMeasureSpec(MeasureSpec.EXACTLY, 35));
}
I have a custom Button and I want to set its dimensions in code (as opposed to in xml), so that users can customize the dimensions. The seemingly obvious way to this is:
public class MyButton extends Button
{
public MyButton(Context context, AttributeSet attrs)
{
super(context, attrs);
int buttonSize = getSize();
setLayoutParams(new LinearLayout.LayoutParams(buttonSize, buttonSize));
}
However, this fails to be generic because it only works if the Button's parent is a LinearLayout. Instead, I tried this:
#Override
protected void onMeasure(int specw, int spech)
{
int spec = MeasureSpec.makeMeasureSpec(getButtonSize()), MeasureSpec.EXACTLY);
super.onMeasure(spec, spec);
}
...which seems to work well. Is anyone aware of any shortcomings to this? or aware of a better way to generically set widget dimensions in code?
Doing it from onMeasure() is a good way to do it (even though your code doesn't work since you're not using the measure spec you've created.) You could also override onFinishInflate() and call getLayoutParams() and change the width and height fields.