Ondraw method not calling in custom view - android

I am making custom view using custom ViewGroup and custom View in Android.
public class Custom_ViewGroup extends ViewGroup
{
public Custom_ViewGroup(Context context)
{
super(context);
addView(new OwnView(context));
}
public Custom_ViewGroup(Context context,AttributeSet attrs)
{
super(context, attrs);
addView(new OwnView(context));
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b)
{
// TODO Auto-generated method stub
}
class OwnView extends View
{
public OwnView(Context context)
{
super(context);
System.out.println("on constructor");
}
#Override
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
System.out.println("ondraw child");
}
}
}
onDraw() method of OwnView class is not calling. constructor of OwnView class called. I have used invalidate() method after adding view, but it did not work.

This is your problem
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b)
{
// TODO Auto-generated method stub
}
Your view is never laid-out, so it will not draw. You need to implement the onLayout method properly. Also, if your ViewGroup contains only a single view, consider using FrameLayout instead of ViewGroup.

Related

Android: Bitmap is not drawn in a View that extends ScrollView and has other views inside it

I am trying to draw a bitmap in a ScrollView with multiple components in it. I made a custom view that extends a ScrollView and this custom view is being used as a parent of multiple views (TextViews mostly, all in all, it's just a big list that has a scrolling feature). In this custom ScrollView I'm calling onDraw and creating a bitmap in it which needs to be drawn on top of the other views in the custom ScrollView. My onDraw is as follows:
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Bitmap bitmap = BitmapFactory.decodeResource(AppData.instance().getResources(),
R.drawable.cabin);
canvas.drawBitmap(bitmap, canvas.getWidth()/3, canvas.getHeight()/3, null);
}
I know that onDraw is getting called each time I scroll, so I assume there's some clash of the views in the layout. Pretty certain that the issue is related to having other views in the custom scrollView, since I also tried using this custom view in a plain activity like so:
DrawerScrollView.java
public class DrawerScrollView extends ScrollView {
int mX;
int mY;
private OnScrollViewListener mOnScrollViewListener;
public DrawerScrollView(Context context) {
super(context);
}
public DrawerScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public DrawerScrollView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public void setOnScrollViewListener(OnScrollViewListener onScrollViewListener) {
mOnScrollViewListener = onScrollViewListener;
}
#Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
mOnScrollViewListener.onScrollChanged(this, l, t, oldl, oldt);
mX = l;
mY = t;
}
public interface OnScrollViewListener {
void onScrollChanged(DrawerScrollView drawerScrollView, int l, int t, int oldl, int oldt);
}
#Override
protected void onAttachedToWindow()
{
setLayerType(LAYER_TYPE_SOFTWARE, null);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Bitmap bitmap = BitmapFactory.decodeResource(AppData.instance().getResources(),
R.drawable.cabin);
canvas.drawBitmap(bitmap, canvas.getWidth()/3, canvas.getHeight()/3, null);
}
}
This is the layout main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<com.blah.DrawerScrollView
android:layout_width="fill_parent"
android:layout_height="fill_parent">
</com.blah.DrawerScrollView>
</LinearLayout>
And here's the activity MainActivity.java
public class MainActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
}
When I run this, the bitmap is being displayed in the Activity. But when the DrawerScrollView has other views in it and has to draw the bitmap on top of them (all the other views are being displayed), bitmap is not there. Btw, another insight is that in this case DrawerScrollView is a child of a DrawerLayout (hence the name).
Any feedback would be greatly appreciated! I assume there might be some trickery in how ScrollView is being rendered, but I'm not sure.
Thanks a lot!

Setting PopupWindow to have a maximum height

I inflate a ListView inside a PopupWindow and I want the popup to behave like this:
wrap the listview when its height is < x
set the popup's height = x when the listiew's height > x (the listview being scrollable)
the popup is showed by a TextWatcher attached to an EditText, as it is meant to show search suggestions.
The adapter underlying the ListView is managed by a custom Loader, started whenever the user has typed something in the EditText.
I tried to override onMeasure() on the listview and pass the measured height to a listener which calls PopupWindow.update(), but this creates a loop since the latter would end up calling the first.
With the following code, the popup wraps the contained ListView as wanted, but the height is not restricted to any value. I need a solution to restrict the height to a maximum value, let's say 300dp.
etFiltro.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) {
/...
}
#Override
public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) {
Log.i("loader","text changed");
filtro = String.valueOf(charSequence);
getSupportLoaderManager().restartLoader(0,null,loaderCallbacks);
if (popupWindow.isShowing()) popupWindow.dismiss();
}
#Override
public void afterTextChanged(Editable editable) {
popupWindow.showAsDropDown(etFiltro);
}
});
private LoaderManager.LoaderCallbacks<Cursor> loaderCallbacks = new LoaderManager.LoaderCallbacks<Cursor>() {
MyListView suggestionList;
SimpleCursorAdapter adapter;
int mHeight;
#Override
public Loader<Cursor> onCreateLoader(int i, Bundle bundle) {
SuggestionLoader loader = new SuggestionLoader(MainActivity.this, databaseConnector, filtro);
loader.setUpdateThrottle(500);
View view = ((LayoutInflater)getSystemService(LAYOUT_INFLATER_SERVICE)).inflate(R.layout.popup_window,null);
suggestionList = (MyListView) view.findViewById(R.id.suggestionList);
adapter = new SimpleCursorAdapter(MainActivity.this,android.R.layout.simple_list_item_1,null,
new String[]{"note"},new int[]{android.R.id.text1},0);
suggestionList.setAdapter(adapter);
suggestionList.setEmptyView(view.findViewById(R.id.tvNoSuggestions));
//ensure previous popup is dismissed
if (popupWindow!=null) popupWindow.dismiss();
popupWindow = new PopupWindow(view,etFiltro.getWidth(),0);
popupWindow.setWindowLayoutMode(0,WindowManager.LayoutParams.WRAP_CONTENT);
popupWindow.setAnimationStyle(0);//0 = no animation; -1 = default animation
Log.i("loader","onCreateLoader");
return loader;
}
#Override
public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor cursor) {
adapter.changeCursor(cursor);
Log.i("loader","onLoadFinished");
}
#Override
public void onLoaderReset(Loader<Cursor> cursorLoader) {
Log.i("loader", "onLoaderReset");
adapter.changeCursor(null);
}
};
I have found the solution by myself. For anyone who will ever stumble upon this problem, the answer is to override the method onMeasure() of the ListView like this:
public class MyListView extends ListView {
public MyListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public MyListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyListView(Context context) {
super(context);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//set your custom height. AT_MOST means it can be as tall as needed,
//up to the specified size.
int height = MeasureSpec.makeMeasureSpec(300,MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec,height);
}
}

How to create a custom LayoutParams to be used on a custom layout?

I'm fairly proficient at creating complex custom layouts based on ViewGroup. The only thing I'm missing is the ability to create my custom LayoutParams. I really need the ability to get the margins and why not create other extra params to pass in to the parent.
How can I go about creating a custom LayoutParam and using it via xml? I tried using a LinearLayout.LayoutParam but it's obviously crashing since the parent is not a LinearLayout. How can I work with LayoutParams on custom layouts?
Update:
As of now I'm sticking with using a FrameLayout and overriding the onMeasure and onLayout functions to do the layout myself. This does provide FrameLayout.LayoutParams. I'm guessing the childs would have to support the custom LayoutParam?
In your custom layout, create a nested class extending ViewGroup.LayoutParams. Then override some methods (all of the required ones are in my example). Here's a stripped-down version of one of my custom layouts:
public class MyLayout extends ViewGroup {
public MyLayout(Context context) {
}
public MyLayout(Context context, AttributeSet attrs) {
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
}
#Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
return p instanceof LayoutParams;
}
#Override
protected LayoutParams generateDefaultLayoutParams() {
return new LayoutParams();
}
#Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new LayoutParams(getContext(), attrs);
}
#Override
protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
return generateDefaultLayoutParams(); // TODO Change this?
}
public static class LayoutParams extends ViewGroup.LayoutParams {
public LayoutParams() {
}
public LayoutParams(int width, int height) {
}
public LayoutParams(Context context, AttributeSet attrs) {
}
}
}
Further explanation: How to create a FlowLayout (thanks for the link Luksprog!)

How to have a Viewgroup containing multiple ImageButton in android?

While creating a custom Viewgroup,is it possible to have multiple Imagebutton within it?
is it possible to place Image buttons in our own position.
If all these are possible,how to call the click listener which extending this viewgroup?
My ViewGroup have to look like this image
EDIT 1:
public class CustomViewGroup extends ViewGroup{
public CustomViewGroup(Context context) {
super(context);
setWillNotDraw(false);
//addImageView();
// TODO Auto-generated constructor stub
}
public CustomViewGroup(Context context, AttributeSet attrs) {
super(context, attrs);
setWillNotDraw(false);
//addImageView();
// TODO Auto-generated constructor stub
}
public CustomViewGroup(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs,
defStyle);
setWillNotDraw(false);
//addImageView();
// TODO Auto-generated constructor stub
}
#Override
protected void onLayout(boolean arg0, int arg1, int arg2, int arg3, int arg4) {
// TODO Auto-generated method stub
System.out.println("onLayout");
}
#Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
drawBackground(canvas);
addImageView();
//super.onDraw(canvas);
}
private void drawBackground(Canvas canvas)
{
Bitmap background =BitmapFactory.decodeResource(getContext().getResources(), R.drawable.shape_quarter_circle);
canvas.drawBitmap(background, 0,0, null);
}
private void addImageView()
{
ImageView imageOne = new ImageView(getContext());
imageOne.setImageDrawable(getContext().getResources().getDrawable(R.drawable.round_icon));
//imageOne.setWillNotDraw(false);
addView(imageOne);
requestLayout();
}
I am trying to draw a background and place some ImageView on the top of the background.
In this code Background image is getting displayed correctly. but i could not see the ImageView drawn upon it.
Am i going in the correct path?
all the things you said are possible.
you just use
yourViewGroup.setOnClickListener()
for your view group click listener.

listening to scroll events horizontalscrollview android

I am trying to listen to the event when the HorizontalScrollView is scrolled. Tried this but it does not print anything.
HorizontalScrollView headerScrollView = new HorizontalScrollView(this);
headerScrollView.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
Log.i("hv1",event.toString());
Log.i("hv1","HELLO");
return false;
}
});
The actual problem is, I want to scroll two HorizontalScrollView at a time..ie; both of them need to scroll simultaneously when atleast one of them scrolled.
any workaround?
I used the answer below and then tried implementing this but I am not sure how I need to use the methods in the class.
TestHorizontalScrollView headerScrollView = (TestHorizontalScrollView) findViewById(R.id.headerHv);
Is this the way I need to point to the hsv element in the layout file?
You may want to try creating your own custom class that extends HorizontalScrollView and overriding the onScrollChanged() function as such
public class TestHorizontalScrollView extends HorizontalScrollView {
public TestHorizontalScrollView(Context context) {
super(context);
}
#Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
// TODO Auto-generated method stub
Log.i("Scrolling", "X from ["+oldl+"] to ["+l+"]");
super.onScrollChanged(l, t, oldl, oldt);
}
}
This overriden function will catch all changes to the scroll position even when the view is not being touched. This should keep your scroll views in sync.
old question, but maybe helpful. You can do something like this:
scrollOne = (HorizontalScrollView)findViewById(R.id.horizontal_one);
scrollTwo = (HorizontalScrollView)findViewById(R.id.horizontal_two);
scrollTwo.setOnTouchListener(new OnTouchListener(){
#Override
public boolean onTouch(View view, MotionEvent event) {
// TODO Auto-generated method stub
int scrollX = view.getScrollX();
int scrollY = view.getScrollY();
scrollOne.scrollTo(scrollX, scrollY);
return false;
}
});
ScrollView with Listener, api < 23
write below in your code
MyHorizontalScrollView scrollView = (MyHorizontalScrollView)view.findViewById(R.id.scrollViewBrowse);
scrollView.setOnScrollChangedListener(new MyHorizontalScrollView.OnScrollChangedListener() {
#Override
public void onScrollChanged(int l, int t, int oldl, int oldt) {
}
});
MyHorizontalScrollView
public class MyHorizontalScrollView extends ScrollView {
public OnScrollChangedListener mOnScrollChangedListener;
public MyHorizontalScrollView(Context context) {
super(context);
}
public MyHorizontalScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyHorizontalScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
if (mOnScrollChangedListener != null) {
mOnScrollChangedListener.onScrollChanged(l, t, oldl, oldt);
}
}
public void setOnScrollChangedListener(OnScrollChangedListener onScrollChangedListener){
this.mOnScrollChangedListener = onScrollChangedListener;
}
public interface OnScrollChangedListener{
void onScrollChanged(int l, int t, int oldl, int oldt);
}
}
* Xml file *
<MyHorizontalScrollView
android:id="#+id/scrollViewBrowse"
android:layout_width="match_parent"
android:layout_height="fill_parent"
android:background="#drawable/backgroung"
android:padding="10dp">
</MyHorizontalScrollView>
To Avoid the the Api Level Problem... follow this.
scrollView.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
#Override
public void onScrollChanged() {
int scrollY = scrollView.getScrollY();
int scrollX = scrollView.getScrollX();
if (scrollY > 500) {
} else {
}
}
});
you can try this, i've tried so many ways, but none of them meet my needs, and then i tried to do it myself, and i succeed , and it seems good , it's smooth.
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal">
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<com.szl.fundlistdemo.WrapContentListView
android:id="#+id/left_lv"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:scrollbars="none"/>
<HorizontalScrollView
android:id="#+id/hscrollview"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<com.szl.fundlistdemo.WrapContentListView
android:id="#+id/right_lv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</HorizontalScrollView>
</LinearLayout>
</ScrollView>
public class WrapContentListView extends ListView{
public WrapContentListView(Context context) {
super(context);
}
public WrapContentListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public WrapContentListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, expandSpec);
}
}
Based #Dipak's answer In kotlin:
binding.scrollList.viewTreeObserver.addOnScrollChangedListener {
val X = binding.scrollList.scrollX
val Y = binding.scrollList.scrollY
}

Categories

Resources