I have a subclass called AuditQuestionEntry that extends LinearLayout. In addition to other fields, this layout contains a button. On my main activity ("AuditActivity") I have multiple instances of AuditQuestionEntry. From AuditActivity, I am setting an OnClickListener for the button in each of the AuditQuestionEntry layouts. In the OnClick method I need to access a parameter that's in the AuditQuestionEntry associated with the button that was clicked.
In the OnClickListener I call getParent() and I can cast that to AuditQuestionEntry. However, when I try to access a parameter from there it returns null (or 0 in my case because it's an int). Basically, it appears that getParent() is returning a new instance of the class and not the actual instance that contains the button.
Layout - audit_question_entry.xml:
<TextView
android:id="#+id/auditQuestion"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Audit Question..."
android:textSize="20dp" />
<Button
android:id="#+id/btnTakeAuditPhoto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Add Photo" >
</Button>
Here's the AuditQuestionEntry.java:
public class AuditQuestionEntry extends LinearLayout {
// Define controls
private TextView auditQuestion;
private RatingBar auditRating;
private EditText auditComment;
private ImageView imageView;
private Button photoButton;
private static final int CAMERA_REQUEST = 1888;
private static final int REQUEST_IMAGE_CAPTURE = 1;
public int AuditQuestionNumber; // NEED TO GET THIS!
private Context _ctx;
public ClosetAuditQuestionObj AuditQuestion;
private String PhotoFolder;
private String FullPhotoPathAndName;
public AuditQuestionEntry(Context context) {
super(context);
// InflateView();
throw new RuntimeException("Valid AuditQuestionNumber must be passed to this class via the XML parameters: workbench:AuditQuestionNumber.");
}
public AuditQuestionEntry(Context context, AttributeSet attrs) {
super(context, attrs);
this._ctx = context;
initAttributes(attrs);
}
// Used to grab the AuditQuestionNumber attribute from the XML declaration
// for this layout
private void initAttributes(AttributeSet attrs) {
TypedArray a = _ctx.obtainStyledAttributes(attrs, R.styleable.AuditQuestionEntry);
AuditQuestionNumber = attrs.getAttributeIntValue("http://schemas.android.com/apk/com.bc.workbench", "AuditQuestionNumber", 0);
a.recycle();
}
Inside AuditActivity onCreate() I have the following:
OnClickListener btnPhotoListener = new OnClickListener() {
#Override
public void onClick(final View v) {
View viewParent = (View) v.getParent();
AuditQuestionEntry clickedAudit = (AuditQuestionEntry) viewParent;
// On the following line, clickedAudit.AuditQuestionNumber is always 0 even though it was initialized and
// displays properly on screen.
Toast.makeText(CurrentContext, "Audit Question #" + clickedAudit.AuditQuestionNumber, Toast.LENGTH_SHORT).show();
}
};
// Set event listener for photo buttons
// =============================================
((Button) AuditQuestion1.findViewById(R.id.btnTakeAuditPhoto)).setOnClickListener(btnPhotoListener);
((Button) AuditQuestion2.findViewById(R.id.btnTakeAuditPhoto)).setOnClickListener(btnPhotoListener);
Where in the XML are you setting the attribute for the AuditQuestionNumber? The constructor is defaulting the value of the class attribute to 0 if the XML being used to inflate the class doesn't include the attribute:
AuditQuestionNumber = attrs.getAttributeIntValue("http://schemas.android.com/apk/com.bc.workbench", "AuditQuestionNumber", 0);
Related
I am new to android development, in fact its my first application. I have created a dynamic layout in my project based on Json. Each object includes an "id" key and some more string keys. every object in my json should be transformed to a cardview inside a recylcerview and each cardview has a button.
My problem is handling these dynamic buttons. Is it possible to determine which button was clicked?
View.id is an integer, and you shouldn't set arbitrary values to it if the View is generated dinamically, what you can use though is View.tag. So you can assign the id defined in the JSON to tag and then check the tag value when the View is clicked. E.g.
val view1 = View(context)
view1.tag = "id from JSON 1"
view1.setOnClickListener(this::onViewClicked)
val view2 = View(context)
view2.tag = "id from JSON 2"
view2.setOnClickListener(this::onViewClicked)
// ...
private fun onViewClicked(view: View){
val jsonId = view.tag as? String
// ...
}
If your min sdk level at least 17, another option would be to generate ids dinamically with View.generateViewId() and store them in a Map together with your JSON ids
Use a Tag to differentiate the buttons. Add OnClickListener to all Button and set different String tag to button.
button.setOnClickListener(this);
Add ClickListener
#Override
public void onClick(View view){
String tag = (String)view.getTag();
if(tag.equals(tag1)){
// action here
}
//.......
.....
}
Yeah pretty straight, all you have to do is to give them a unique id
I assume you must have a JSON array for dynamic buttons creation.
sample code
public Button createButton(Context context,String text,int buttonNo){
//here set the properties
Button bt = new Button(context);
bt.setText(text);
bt.setId(buttonNo);
bt.setTag(buttonNo);
bt.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View view){
//handle the button click, unique id , you can use to differentiate
int id = view.getId();
}
});
return bt;
}
call this above method in for loop or as many times you want to create.
you can also set a tag as mentioned above to store more information.
Finally I put the OnClickListener in onBindViewHolder event inside my customAdapter class as below:
public class CustomAdapter extends RecyclerView.Adapter<CustomAdapter.ViewHolder> implements View.OnClickListener {
private Context context;
private List<MyData> my_data;
public CustomAdapter(Context context, List<MyData> my_data) {
this.context = context;
this.my_data = my_data;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.card,parent,false);
return new ViewHolder(itemView);
}
#Override
public void onBindViewHolder(final ViewHolder holder, int position) {
holder.match_date.setText(my_data.get(position).getMatch_date());
holder.home_name.setText(my_data.get(position).getHome_name());
holder.away_name.setText(my_data.get(position).getAway_name());
holder.button.setId(my_data.get(position).getId());
holder.button.setTag(my_data.get(position).getStringId());
holder.button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int intHomeGoals = Integer.parseInt(holder.edtHomeGoals.getText().toString());
int intAwayGoals = Integer.parseInt(holder.edtAwayGoals.getText().toString());
if (intHomeGoals == intAwayGoals)
{
Toast.makeText(context, "00000", Toast.LENGTH_SHORT).show();
}
}
});
Glide.with(context).load(my_data.get(position).getHome_logo()).into(holder.home_logo);
Glide.with(context).load(my_data.get(position).getAway_logo()).into(holder.away_logo);
}
I want to achieve the following abilities:
Select only one child View inside a GridLayout each time by long clicking it.
A click on the GridLayout or any ancestor parent in the visual hierarchy will deselected selected child View if one already selected.
The problem is when when registering a View.OnLongClickListener callback to child View, neither parent GridLayout nor any ancestor registered callbacks (either View.OnClickListener or View.onTouchEvent) called when clicking on them.
How can I get a selected child inside a GridLayout similar to either AdapterView.OnItemSelectedListener or AdapterView.OnItemLongClickListener and solve the above mentioned problem?
What about storing a "selected" view as a global variable, and removing it when its focus changes? By playing with focusable, focusableInTouchMode and onClick listeners, you could have the right results. I'm not sure that's the best solution, but it works.
What you will need:
A global View variable: the GridLayout's child long clicked, as selected.
(optional) A custom parent container as any ViewGroup: it will set the focusable listeners on all its children [*]. In my tests, I used a LinearLayout and a RelativeLayout.
[*] If you don't use the optional parent custom Class, you have to set android:focusable="true" and android:focusableInTouchMode="true" on all children of the parent ViewGroup. And you'll have to set OnClickListener in order to call removeViewSelected() when the parent ViewGroup is clicked.
Adding Click listeners for GridLayout children: which updates the selected view.
Implementing a Focus listener: which removes the selected view if it's losing focus.
It will handle all focus change state on parent and child hierarchy, see the output:
I used the following pattern:
CoordinatorLayout --- simple root group
ParentLayout --- aka "parentlayout"
Button --- simple Button example
GridLayout --- aka "gridlayout"
FloattingActionButton --- simple Button example
Let's preparing the selected View and its update methods in the Activity:
private View selectedView;
...
private void setViewSelected(View view) {
removeViewSelected();
selectedView = view;
if (selectedView != null) {
// change to a selected background for example
selectedView.setBackgroundColor(
ContextCompat.getColor(this, R.color.colorAccent));
}
}
private View getViewSelected() {
if (selectedView != null) {
return selectedView;
}
return null;
}
private void removeViewSelected() {
if (selectedView != null) {
// reset the original background for example
selectedView.setBackgroundResource(R.drawable.white_with_borders);
selectedView = null;
}
// clear and reset the focus on the parent
parentlayout.clearFocus();
parentlayout.requestFocus();
}
On each GridLayout child, add the Click and LongClick listeners to update or remove the selected view. Mine were TextViews added dynamically, but you could easily create a for-loop to retrieve the children:
TextView tv = new TextView(this);
...
gridlayout.addView(tv);
tv.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
removeViewSelected();
}
});
tv.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View view) {
setViewSelected(view);
return true;
}
});
Set the FocusChange listener on the parent container:
parentlayout.setOnFocusChangeListener(new View.OnFocusChangeListener() {
#Override
public void onFocusChange(View view, boolean hasFocus) {
View viewSelected = getViewSelected();
// if the selected view exists and it lost focus
if (viewSelected != null && !viewSelected.hasFocus()) {
// remove it
removeViewSelected();
}
}
});
Then, the optional custom ViewGroup: it's optional because you could set the focusable state by XML and the clickable listener dynamically, but it seems easier to me. I used this following custom Class as parent container:
public class ParentLayout extends RelativeLayout implements View.OnClickListener {
public ParentLayout(Context context) {
super(context);
init();
}
public ParentLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public ParentLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
// handle focus and click states
public void init() {
setFocusable(true);
setFocusableInTouchMode(true);
setOnClickListener(this);
}
// when positioning all children within this
// layout, add their focusable state
#Override
protected void onLayout(boolean c, int l, int t, int r, int b) {
super.onLayout(c, l, t, r, b);
final int count = getChildCount();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
child.setFocusable(true);
child.setFocusableInTouchMode(true);
}
// now, even the Button has a focusable state
}
// handle the click events
#Override
public void onClick(View view) {
// clear and set the focus on this viewgroup
this.clearFocus();
this.requestFocus();
// now, the focus listener in Activity will handle
// the focus change state when this layout is clicked
}
}
For example, this is the layout I used:
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout ...>
<com.app.ParentLayout
android:id="#+id/parent_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal">
<Button
android:id="#+id/sample_button"
android:layout_width="250dp"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_alignParentBottom="true"
android:text="A Simple Button"
android:layout_marginTop="20dp"
android:layout_marginBottom="20dp"/>
<android.support.v7.widget.GridLayout
android:id="#+id/grid_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerHorizontal="true"
android:layout_above="#id/sample_button" .../>
</com.app.ParentLayout>
<android.support.design.widget.FloatingActionButton .../>
</android.support.design.widget.CoordinatorLayout>
Hope this will be useful.
Use the following code :
int last_pos = -1;
GridLayout gridLayout;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
gridLayout = (GridLayout) findViewById(R.id.gridLayout);
int child_count = gridLayout.getChildCount();
for(int i =0;i<child_count;i++){
gridLayout.getChildAt(i).setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View view) {
//Deselect previous
if(last_pos!=-1) gridLayout.getChildAt(last_pos).setSelected(false);
//Select the one you clicked
view.setSelected(true);
last_pos = gridLayout.indexOfChild(view);
return false;
}
});
}
//Remove focus if the parent is clicked
gridLayout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
gridLayout.getChildAt(last_pos).setSelected(false);
}
});
So I've got this problem in my custom view. I'm trying to create a custom RelativeLayout with an infinite scrolling animation. To achieve this, I've created a layout backgroundscrollrelativelayout.xml like so:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/mainTile"/>
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/topTile" />
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/leftTile" />
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/diagonalTile" />
</RelativeLayout>
The idea is that the ImageViews will translate their position on an animation update callback.
I've created the BackgroundScrollRelativeLayout.java like so:
public class BackgroundScrollRelativeLayout extends RelativeLayout {
final int layoutToUse = R.layout.backgroundscrollrelativelayout;
final int mainTileId = R.id.mainTile;
final int leftTileId = R.id.leftTile;
final int topTileId = R.id.topTile;
final int diagonalTileId = R.id.diagonalTile;
final float valueStart = 0.0f;
final float valueEnd = 1.0f;
final long animationDuration = 50000L;
private Context mContext;
private ValueAnimator scrollAnimator;
private ImageView mainTile;
private ImageView leftTile;
private ImageView topTile;
private ImageView diagonalTile;
public BackgroundScrollRelativeLayout(Context context) {
super(context);
mContext = context;
acquireViewsInLayout();
initializeAnimator();
scrollAnimator.start();
}
public BackgroundScrollRelativeLayout(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
acquireViewsInLayout();
initializeAnimator();
scrollAnimator.start();
}
public BackgroundScrollRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mContext = context;
acquireViewsInLayout();
initializeAnimator();
scrollAnimator.start();
}
#Override
public void setBackgroundColor(int color) {
// Not supported
}
#Override
public void setBackgroundResource(int resid) {
// Not supported
}
#Override
public void setBackground(Drawable background) {
mainTile.setBackground(background);
leftTile.setBackground(background);
}
/*
#Override
public void setBackgroundDrawable(Drawable background) {
//super.setBackgroundDrawable(background);
//mainTile.setBackground(background);
//leftTile.setBackground(background);
}*/
// Intent: Inflate the layout associated with this View
private void inflateLayout(){
// TO inflateLayout, we connect the inflater to context, then we call inflate the layout associated
// with this view
//LayoutInflater inflater = LayoutInflater.from(mContext);
//inflater.inflate(layoutToUse, this);
inflate(getContext(), layoutToUse, this);
}
// Intent: Find all Views in Layout
private void findAllViewsById(){
mainTile = (ImageView)this.findViewById(mainTileId);
leftTile = (ImageView)this.findViewById(leftTileId);
topTile = (ImageView)this.findViewById(topTileId);
diagonalTile = (ImageView)this.findViewById(diagonalTileId);
}
// Intent: Concretely acquire all Views in Layout
private void acquireViewsInLayout(){
// TO acquireViewsInLayout, we inflate the layout,
// then we find the view of each known view id and save the view
inflateLayout();
findAllViewsById();
}
// Intent: Initialize animator properties
private void initializeAnimator(){
// TO initializeAnimator, we set how the animator will keep track of animation,
// then we set the animation repeat type, then we set the type of interpolation,
// then we set the animation duration, then we apply animation update listener
scrollAnimator = ValueAnimator.ofFloat(valueStart, valueEnd);
scrollAnimator.setRepeatCount(ValueAnimator.INFINITE);
scrollAnimator.setInterpolator(new LinearInterpolator());
scrollAnimator.setDuration(animationDuration);
addScrollAnimatorUpdateListener();
}
// Intent: Add an update listener to the scroll animator
private void addScrollAnimatorUpdateListener(){
// TO addScrollAnimatorUpdateListener, we add an update listener to scroll animator
scrollAnimator.addUpdateListener( new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
// Do something...
updateScrollAnimation();
}
});
}
private void updateScrollAnimation(){
float progress = (float)scrollAnimator.getAnimatedValue();
float widthOfTile = mainTile.getWidth();
float moveInXAxis = widthOfTile * progress;
mainTile.setTranslationX(moveInXAxis);
leftTile.setTranslationX(moveInXAxis - widthOfTile);
// Ignore the rest for now
topTile.setTranslationY(-1.0f);
diagonalTile.setTranslationX(-1.0f);
diagonalTile.setTranslationY(-1.0f);
}
}
I use my custom view like so in an activity:
<RelativeLayout 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:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
tools:context="com.martianstudio.adivinaque.AppSettingsActivity">
<com.martianstudio.adivinaque.BackgroundScrollRelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/main_activity_animation_icon" />
</RelativeLayout>
The goal of this is to allowing the user to specify the background that they want to scroll with android:background. I do not want the parent RelativeLayout to take on this drawable as a background. Instead, I want the ImageViews to set the drawable as the background. I have overridden the setBackground in my custom view so that only the ImageViews set the drawable as the background and not the RelativeLayout (as it would by default).
#Override
public void setBackground(Drawable background) {
mainTile.setBackground(background);
leftTile.setBackground(background);
}
However, I get this error:
java.lang.NullPointerException at com.martianstudio.adivinaque.BackgroundScrollRelativeLayout.setBackground
This says that mainTile in setBackground has not been set (it is null). In my findAllViewsById() I explicitly write this.mainTile = (ImageView)this.findViewById(mainTileId);, where mainTileId = R.id.mainTile, and I inflate the layout in inflateLayout(). Both of these methods are called in the constructors of the class. It seems to me that for some reason, it cannot find the ImageView with the mainTile Id when I override the setBackground method like I do. If I don't override the setBackground method, I do not get a NullPointerException, however, it defaults to the default setBackground method, which is what I do not want. Am I missing somthing?
Any help will be appreciated :). Thank you!
I'm making an app that just displays a clock, but I want is so that everytime a user touches the screen it changes the color of the text (from a list of preselected colors in a colors.xml file) but I haven't got a clue where to start. Can someone point me in the right direction?
Here's the main activity:
public class MainActivity extends Activity {
private static final Random RANDOM = new Random();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
setContentView(R.layout.activity_main);
Handler handler = new RandomMoveHandler((TextView) findViewById(R.id.digitalClock1));
handler.sendEmptyMessage(0);
}
// Make the handler subclass static because of this: http://stackoverflow.com/a/11408340/111777
private static class RandomMoveHandler extends Handler {
private final WeakReference<TextView> textViewWeakReference;
private RandomMoveHandler(TextView textView) {
this.textViewWeakReference = new WeakReference<TextView>(textView);
}
#Override
public void handleMessage(Message msg) {
TextView textView = textViewWeakReference.get();
if (textView == null) {
Log.i(TAG, "WeakReference is gone so giving up.");
return;
}
int x = RANDOM.nextInt(350 - 100);
int y = RANDOM.nextInt(800 - 100);
Log.i(TAG, String.format("Moving text view to (%d, %d)", x, y));
textView.setX(x);
textView.setY(y);
//change the text position here
this.sendEmptyMessageDelayed(0, 30000);
}
}
private static final String TAG = MainActivity.class.getSimpleName();
}
and here's the layout xml:
<AbsoluteLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
tools:context=".MainActivity"
android:background="#color/black" >
<DigitalClock
android:id="#+id/digitalClock1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="DigitalClock"
android:textColor="#color/ics_blue"
android:textSize="28sp" />
I haven't making deal with DigitalClock but I think, at first, you should reference DigitalClock variable, not TextView. And second, to intercept touch event you need to override onTouckEvent method of your activity, it will callback everytime user touches the screen.
You should follow these steps
Use a TimerTask to.continusly show the time
Implement a touchlistener on that clock view
like this
view.setOnTouchListener
Make an array Colors like this
int[] colr={Color.BLACK,Color.BLUE};
and use random index in your touch event andset it as your color of the view
I'm unable to call methods of a custom view ("canvasview") from the Activity that sets the layout including the view. I can't even call canvasview's "getters" from the activity.
Also, I'm passing the view to a custom class (that does not extend Activity), and I can't call canvasview's methods also from my custom class.
I'm not sure what I'm doing wrong...
GameActivity.java:
public class GameActivity extends Activity implements OnClickListener
{
private View canvasview;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.game_layout);
canvasview = (View) findViewById(R.id.canvasview);
// Eclipse displays ERROR con those 2 method calls:
int w = canvasview.get_canvaswidth();
int h = canvasview.get_canvasheight();
(...)
game_layout.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/LinearLayout2"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".GameActivity" >
(...)
<com.example.test.CanvasView
android:id="#+id/canvasview"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
CanvasView.java:
public class CanvasView extends View
{
private Context context;
private View view;
private int canvaswidth;
private int canvasheight;
public CanvasView(Context context, AttributeSet attrs)
{
super(context, attrs);
this.context = context;
this.view = this;
}
#Override
protected void onSizeChanged(int width, int height,
int old_width, int old_height)
{
this.canvaswidth = width;
this.canvasheight = height;
super.onSizeChanged(width, height, old_width, old_height);
}
public int get_canvaswidth()
{
return this.canvaswidth;
}
public int get_canvasheight()
{
return this.canvasheight;
}
I'm quite confused with this :?
I also have another class (it does not extend "Activity") that receives in the constructor a reference to canvasview and is also unable to "resolve" it :?
Thanks, sorry if the question is too obvious, I'm starting with Java and those kind of things are quite confusing to me ...
EDIT:
While at bed (03:00AM), thinking about it, I've noticed that Eclipse marks the line as an error because the View object does not really have the method get_canvaswidth(). Only the child "CanvasView" method has it. Thus, my problem can be solved with upcast:
int w = ((CanvasView) canvasview).get_canvaswidth();
I mean that I receive a view as parameter, but as I now it's really a view child, I should be able to use upcast to call "child's" methods. Now eclipse does not generate errors but w and h always report 0 :-? . I've also tested of not using upcast as has been suggested in an answer, and sending and receiving CanvasView objects in the calls and I also get 0 for both :?
private View canvasview;
No matter what is stored in canvasview you can only call methods defined by the variable type. You need to change this line.
private CanvasView canvasview;