I have a small issue here is the code i have so far:
public class MainActivity extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new GraphicView(this));
TextView mainLabel = (TextView)findViewById(R.id.title);
mainLabel.setText("Android Circle Path");
}
static class GraphicView extends View{
public GraphicView(Context context){
super(context);
}
#Override
public void onDraw(Canvas canvas){
}
}
}
And the main.xml file:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="30dip"
>
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginLeft="20dip"
android:layout_marginRight="20dip">
<TextView
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginBottom="20dip"
android:textSize="24.5sp"
android:id="#+id/title">
</TextView>
</LinearLayout>
</LinearLayout>
When i try to set the text in the onDraw() function the app crashes, what am i doing wrong?
A view's onDraw method should be used to render to that view alone. You should not be calling UI update methods in another view. Move the code for setting the title into your activity's onCreate method, since that only needs to be done once. Then just draw your circle in your custom view. Also, since your custom view does not implement ViewParent, it shouldn't be used to hold the title TextView. Reorganize your xml to move the title view outside the GraphicsView
TextView is a local variable created in the onCreate method of that Activity. So the scope of TextView is confined within that onCreate method only. You can't access it outside that method. Moreover what you are trying to do is not the right way, inside GraphView's onDraw method you are supposed to draw the GraphView components alone, not the outside views or anything else.
Related
I have an template view it looks like ;
template.xml :
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/contentLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/view_background"
android:gravity="center_vertical"
tools:context=".MainActivity" >
<Button
android:id="#+id/btnMultiple"
android:layout_width="0dp"
android:layout_height="150dp"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:layout_weight="1"
android:background="#drawable/button_backgroundblue"
android:onClick="btnMultiple_clicked"
android:tag="4"
android:text="#string/mc_abcd"
android:textColor="#drawable/button_textcolor"
android:textSize="#dimen/h2" />
I'm creating this view programmatically and then i'm adding this View into ViewFlipper as like ;
activity_main.java
public void btnCreateView_clicked(View view) {
ViewFlipper viewFlipper = (ViewFlipper)findViewById(R.id.flipper);
View myView = LayoutInflater.from(getApplicationContext()).inflate(R.layout.template, viewFlipper, false);
viewFlipper.addView(myView);
viewFlipper.showNext(); // Showing lastly created myView successfully. }
Also my activity has public function as like ;
activity_main.java
public void btnMultiple_clicked(View view) {
//Save the world !
}
When i press btnCreateView button from first view it's successfully creating template (myView) and then adding it into ViewFlipper. When i click btnMultiple i'm getting error and my application crashes ; "Could not find a method btnMultiple_clicked(View) in the activity class android.app.Application for onClick handler on view class android.widget.Button with id 'btnMultiple'" i'm sure there is existing method btnMultiple_clicked(View).
PS! If i add listener to btnMultiple programmatically it's gonna work but i wonder why "android:onClick="btnMultiple_clicked"" doesn't work ?
Android looks for the method defined in the onClick attribute in the Activity. The problem in your code is that you used for the LayoutInflater initialization the Application's Context and not the Context of the Activity so the method will not be found there(the Context is passed to the View). Use:
View myView = LayoutInflater.from(this).inflate(R.layout.template, viewFlipper, false);
or any other reference that points to the Activity where those buttons will be used.
This is a good example why you should use in most cases the Context of the Activity.
So I'm experimenting with implementing an MVC pattern in Android where my views are subclassed from RelativeLayout, LinearLayout, ScrollView, etc... It's working until I try to get a hold of a view within my view. I get an NPE. I've tried accessing the view in order to set the onClickListener in the constructor and also in onAttachedToWindow(), but I get the NPE in both places.
For example, here's a view class:
public class ViewAchievements extends LinearLayout
{
private RelativeLayout mRelativeLayoutAchievement1;
public ViewAchievements(Context context, AttributeSet attrs)
{
super(context, attrs);
mRelativeLayoutAchievement1 = (RelativeLayout) findViewById(R.id.relativeLayout_achievement1);
mRelativeLayoutAchievement1.setOnClickListener((OnClickListener) context); //NPE on this line
}
#Override
protected void onAttachedToWindow()
{
super.onAttachedToWindow();
mRelativeLayoutAchievement1.setOnClickListener(mOnClickListener); //Also get NPE on this line
}
}
Can someone please tell me the proper way to get a hold of my subviews, in this case mRelativeLayoutAchievement1?
Here's an XML snippet:
<com.beachbody.p90x.achievements.ViewAchievements xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#color/gray_very_dark"
android:orientation="vertical" >
<!-- kv Row 1 -->
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="0dp"
android:orientation="horizontal"
android:layout_weight="1"
android:baselineAligned="false">
<RelativeLayout
android:id="#+id/relativeLayout_achievement1"
style="#style/linearLayout_achievement"
android:layout_width="0dp"
android:layout_height="fill_parent"
android:layout_margin="#dimen/margin_sm"
android:layout_weight="1" >
<TextView
android:id="#+id/textView_achievement1"
style="#style/text_small_bold_gray"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="#dimen/margin_large"
android:text="1/20" />
</RelativeLayout>
...
And here's how I'm creating the view from my Activity:
public class ActivityAchievements extends ActivitySlidingMenu
{
private ViewAchievements mViewAchievements;
#Override
public void onCreate(Bundle savedInstanceState)
{
mViewAchievements = (ViewAchievements) View.inflate(this, R.layout.view_achievements, null);
setContentView(mViewAchievements);
...
You're trying to get the child views during the view's constructor. Since they are child views, they haven't been inflated yet. Can you move this code out of the constructor, possibly into View.onAttachedToWindow()?
http://developer.android.com/reference/android/view/View.html#onAttachedToWindow()
I've tried to approach this from different angles without luck. Maybe asking a general question can help.
Technically I'm using osmdroid's MapView implementation, not Google's Maps API, but I think this question is a more general programmatic Views vs main_activity.xml defined views in onCreate.
Basically in my MainActivity if I onCreate a View, like MapView, then set it as the ContentView programmatically, I have to also programmatically add in any other Views I want to display in my app:
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.mapView = new MapView(this, 256);
...
this.setContentView(this.mapView);
}
If I attempt to set the ContentView as activity_main, the MapView can't be adjusted onCreate. Maybe I'm missing something: (note that I have methods that handle loading a custom offline tile set, and place markers on the map, etc...)
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
this.mapView = (MapView)findViewById(R.id.mapview);
...
this.intializeMapTiles();
this.mapView.setBuiltInZoomControls(true);
this.mapView.setMultiTouchControls(true);
this.mapController.setCenter(new GeoPoint((int)(50.349622 * 1E6), (int)(-71.823700 *1E6)));
...
this.mapView.invalidate();
}
Here's my activity_main.xml in this case:
<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"
tools:context=".MainActivity" >
<org.osmdroid.views.MapView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/mapview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:clickable="false"/>
</RelativeLayout>
When trying to get the MapView from the ContentView (acitivty_main), none of my method changes to it make any affect. It's as if I don't have access the exact MapView that's being rendered. I've tried invalidating my MapView but it doesn't matter. I get a default looking and behaving MapView.
The reason I'm trying to solve this is naturally I'm looking for my app to include more than a single MapView. I would like to include a SlidingDrawer, or some method of displaying a View with buttons that is only displayed when you long press on a map marker. (note that I have Toast pop ups being displayed on map marker long presses, so I'm good in this regard)
I'd have to add these other Views (SlideingDrawer, etc...) programmatically and not from the main_activity.xml. Even that has a catch-22, where the SlidingDrawer constructor needs an AttributeSet from xml that's painful to build yourself. (I tried) Then you also have to worry about the layout as well.
Anyone have any suggestions? General or otherwise? Thanks!
This might actually be useful to others for a number of reasons.
If you're stuck having to use a View that can only be configured the way to need via its programmatic constructor (e.g. you could just include the View from your activity_main.xml, but the View isn't what you need it to be unless you construct it yourself, like with offline tile maps using OSMDroid's MapView) then you're stuck extending that View and implementing that View's constructor that includes the AttributeSet. The AttributeSet is basically a structure parsed from the activity_main.xml xml for that view. That constructor will be called automatically in Activity when you this.setContentView(R.layout.activity_main) from onCreate(). So any custom constructor stuff needs to go in that constructor for your extended View.
For example, I had to extend the OSMDroid MapView, then implement my offline map tile source from the super entirely. NOTE you have to super() the 1st line in an extended constructor because object methods aren't available until after the inherited constructor is complete, so any super() method calls have to be to static methods.
public class FieldMapView extends MapView {
public FieldMapView(Context context, AttributeSet attrs) throws Exception {
super(
context,
256,
new DefaultResourceProxyImpl(context),
FieldMapView.getOfflineMapProvider(context, MainActivity.mapTileArchiveFilename),
null,
attrs);
this.setUseDataConnection(false);
this.setBuiltInZoomControls(false);
this.setMultiTouchControls(true);
}
Then in my activity_main.xml I point to the extended version of the View: (e.g. FieldMapView)
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/rootview"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" >
<com.test.FieldMapView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/mapview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:clickable="false"/>
So now I have my extended View taking care of any programmatic style requirements, how did I get a SlidingDrawer working with it? I created a SlidingDrawer with a 0dip height View as the handle. I then included a LinearLayout that contains buttons, whatever you want, etc...
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/rootview"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" >
<com.test.FieldMapView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/mapview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:clickable="false"/>
<SlidingDrawer
android:layout_width="wrap_content"
android:id="#+id/slidingDrawerActions"
android:content="#+id/action_content"
android:padding="10dip"
android:layout_height="75dip"
android:handle="#+id/handle2"
android:layout_alignBottom="#id/mapview"
android:orientation="vertical"
android:clickable="false">
<LinearLayout
android:layout_width="wrap_content"
android:id="#+id/action_content"
android:orientation="horizontal"
android:gravity="center"
android:padding="10dip"
android:background="#FF999999"
android:layout_height="wrap_content"
android:clickable="false">
<View
android:id="#id/handle2"
android:layout_width="0dip"
android:layout_height="0dip" />
<ImageButton
android:id="#+id/chatActionButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="2dip"
android:src="#drawable/ic_action_edit"
android:text="Chat">
</ImageButton>
</LinearLayout>
</SlidingDrawer>
</RelativeLayout>
In my MainActivity onCreate() I just find the SlidingDrawer View and assign any listeners accordingly. For opening the drawer when long pressing a Marker in the MapView (this is getting more OSMDroid specific now) I naturally have an OnItemGestureListener to open the drawer:
class NodeGestureListener implements OnItemGestureListener<NodeOverlayItem> {
#Override
public boolean onItemLongPress(int index, NodeOverlayItem node) {
if(slidingDrawerActions.isOpened() || slidingDrawerActions.isMoving()) {
return false;
}
slidingDrawerActions.animateOpen();
return false;
}
The tricky part is I wanted to close it via a click on the MapView (not by touching a close button that takes up space) so I had to assign SlidingDrawer.OnDrawerOpenListener and OnDrawerCloseListener classes. They simply flipped a boolean indicating if the drawer was open or closed. I then set a simple onClickListener for the MapView that closed the drawer if it was open based on the isActionDrawerOpen set by the SlidingDrawer listeners.
public void onCreate(final Bundle savedInstanceState) {
...
this.mapView.setOnClickListener(new MapViewClickListener());
...
this.slidingDrawerActions = (SlidingDrawer)findViewById(R.id.slidingDrawerActions);
this.slidingDrawerActions.setOnDrawerOpenListener(new SlidingDrawerOpenListener());
this.slidingDrawerActions.setOnDrawerCloseListener(new SlidingDrawerCloseListener());
...
}
...
private boolean isActionDrawerOpen = false;
class SlidingDrawerOpenListener implements SlidingDrawer.OnDrawerOpenListener {
#Override
public void onDrawerOpened() {
isActionDrawerOpen = true;
}
}
class SlidingDrawerCloseListener implements SlidingDrawer.OnDrawerCloseListener {
#Override
public void onDrawerClosed() {
isActionDrawerOpen = false;
}
}
private boolean skippedMapViewClickListener = false;
class MapViewClickListener implements OnClickListener {
public void onClick(View view) {
if(isActionDrawerOpen) {
if(skippedMapViewClickListener) {
slidingDrawerActions.animateClose();
skippedMapViewClickListener = false;
} else {
skippedMapViewClickListener = true;
}
}
}
}
Note the skippedMapViewClickListener boolean. The problem I had was that the MapView OnClickListener would be called immediately after the SlidingDrawer listener when long pressing the Marker. Meaning the long press would be considered a MapView click, plus the long press itself would open the drawer before OnClickListener was called, so OnClickListener would always see the drawer as open, and would close it. What I did was effectively skip the 1st onClick this way, so the drawer would stay open until you clicked on the MapView. Seems to work great.
I hope this helps someone. There are like 4 problems I solved with this approach.
My problem is communication between custom GameView (extends SurfaceView) and TextView: I want to set TextView's text from inside of the GameView.
In main activity i'm using this layout file, it should explain structure of my app:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#00ff00"
>
<TextView
android:id="#+id/scoreTV"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="score: 0"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="back"
android:layout_alignParentRight="true" />
</RelativeLayout>
<org.gk.grApp.GameView
android:id="#+id/gameplayScreen"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
/>
</LinearLayout>
I can't change TextView's text in my GameView object, because it's impossible to touch UI thread from another.
Handler doesn't work too, because i can't give a handler's reference to GameView's constructor, that is performed after loading this xml file (a read about default constructor for xml files eg here How can I use GLSurfaceView in a LinearLayout together with other Views, such as TextView or Button?).
Do you have any idea what I should do now? Maybe my deduction is wrong, so please, tell me about this.
EDIT: I changed my xml file, instead of GameView I have now:
<LinearLayout
android:id="#+id/gameplayScreen"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
</LinearLayout>
Also I added an argument (third) into constructor's signature:
public GameView(Context context, AttributeSet as, Handler h) { ... }
and changed my onCreate in GameplayActivity:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.gameplay);
LinearLayout ll = (LinearLayout)findViewById(R.id.gameplayScreen);
GV = new GameView(this, null, scoreHandler);
ll.addView(GV);
}
It works, now I can set TextView's text but after clicking on the back button another exception is thrown:
"Performing pause of activity that is not resumed: {org.gk.grApp/org.gk.grApp.MainMenuActivity}". I just started searching information about this.
First make a reference to the TextView on the activity level:
TextView txv;
In onCreate assign this reference:
txv = (TextView) findViewById(R.id.MyTextView);
Then make a method in your Activity after onCreate like this:
public void setTextView(final String txt){
MyActivity.this.runOnUiThread(new Runnable() {
public void run() {
txv.setText(txt);
}
});
}
Then you make a call from your custom view:
((MyActivity) getContext()).setTextView(str);
my problem I know is simple, but for the life of me I cant get an answer...
I have a LinearLayout that got a scrollview inside it... in the scroll view (in code) it should call a custom ScrollView to show on the application... it only shows black, nothing in it... here is my code
first the xml... simple LL with a textview, scrollview and button
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView android:layout_height="wrap_content"
android:id="#+id/textView1"
android:layout_width="wrap_content"
android:text="test field" android:layout_alignParentTop="true"/>
<ScrollView android:layout_width="600dp"
android:layout_height="300dp"
android:id="#+id/v1"
android:fillViewport="true"/>
<Button android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/gfx"
android:text="Gfx"
></Button>
</LinearLayout>
Now the onCreate of that activity
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
cw = getWindowManager().getDefaultDisplay().getWidth();
ch = getWindowManager().getDefaultDisplay().getHeight();
v = Draw2d findViewById(R.id.v1);
context = this;
setContentView(R.layout.gfx);
mDbHelper = new DbAdapter(this);
mDbHelper.open();
}
Draw2d is the class of the custom scrollView declared like so (it does have onDraw and everything. If I set the contentview to the Draw2d object directly it'll show no problem, but I need it withen a LL)
public static class Draw2d extends ScrollView{
public Draw2d(Context context) {
super(context);
}
Please any help is appreciated
Maybe because of you have nothing in the ScrollView? Try putting something in it. Check this link, you see there is another linear layout in the scrollview with "30px" layout height making the scrollview appear. If the layout_height was wrap_content, you would not see the scrollview if there is enough place on the screen for it.