What I would like to accomplish is to, at runtime, place a button in the middle of the screen, as the very top layer, overlaying anything below it. (It's not big, so it will not completely cover the screen, just whatever happens to be below it.)
I looked at creating a custom dialog, however that blocks all other user input. I want all of the views below this new button to act normally and respond to the user, but I just want to add (and later remove) the button above everything.
Hopefully that makes sense. I'm just wondering what might be the best approach to look into?
Use a FrameLayout, with the button as it's 2nd child. Set it to GONE when you don't want it visible.
I had to overlay a simple layout programmatically on top of any visible activity. Normal activity layout xmls don't know anything about the overlay. Layout had one textview component but could have any structure you see fit. This is my overlay layout.
res/layout/identity.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/identitylayout"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:layout_centerInParent="true" >
<TextView
android:id="#+id/identityview"
android:padding="5dp"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:textColor="#FFFFFF" android:background="#FF6600"
android:textSize="30dp"
/>
</RelativeLayout>
Overlay is shown on top of the existing content, after timeout is deleted from the screen. Application calls this function to display overlay.
private void showIdentity(String tag, long duration) {
// default text with ${xx} placeholder variables
String desc = getString(R.string.identity);
desc = desc.replace("${id}", reqId!=null ? reqId : "RequestId not found" );
desc = desc.replace("${tag}", tag!=null ? tag : "" );
desc = desc.trim();
// get parent and overlay layouts, use inflator to parse
// layout.xml to view component. Reuse existing instance if one is found.
ViewGroup parent = (ViewGroup)findViewById(R.id.mainlayout);
View identity = findViewById(R.id.identitylayout);
if (identity==null) {
LayoutInflater inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
identity = inflater.inflate(R.layout.identity, parent, false);
parent.addView(identity);
}
TextView text = (TextView)identity.findViewById(R.id.identityview);
text.setText(desc);
identity.bringToFront();
// use timer to hide after timeout, make sure there's only
// one instance in a message queue.
Runnable identityTask = new Runnable(){
#Override public void run() {
View identity = findViewById(R.id.identitylayout);
if (identity!=null)
((ViewGroup)identity.getParent()).removeView(identity);
}
};
messageHandler.removeCallbacksAndMessages("identitytask");
messageHandler.postAtTime(identityTask, "identitytask", SystemClock.uptimeMillis()+duration);
}
Timer messageHandler is member of main Activity instance (private Handler messageHandler) where I put all scheduled tasks. I am using Android 4.1 device lower than that I don't know what happens.
Related
I am working on a React Native implementation of the Bitmovin player using their Android SDK. At this stage, I'm not sure how specific this is to the Bitmovin player, but as they don't officially support React Native at this stage, I want to ask about this on SO first. This is a React Native UI Component with a custom view, using a layout file. I am trying to present a subtitle view on top of a player view, and I have based my layout on Bitmovin's simple examples. In fact I have simplified the layout even further:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical">
<com.bitmovin.player.PlayerView
android:id="#+id/bitmovinPlayerView"
app:shutter_background_color="#android:color/transparent"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/bootsplash_background">
<com.bitmovin.player.SubtitleView
android:id="#+id/bitmovinSubtitleView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:foregroundGravity="center" />
</com.bitmovin.player.PlayerView>
</LinearLayout>
This presents the SubtitleView at the top of the screen. Nothing I have tried so far presents the SubtitleView at the bottom of the screen in the more common position. I have experimented with every single parameter on all of these elements, as far as I can tell. Here is the code that initialises the view:
public void init() {
inflate(context, R.layout.player_container, this);
StyleConfig styleConfig = new StyleConfig();
styleConfig.setUiEnabled(false);
PlayerConfig playerConfig = new PlayerConfig();
playerConfig.setStyleConfig(styleConfig);
playerView = findViewById(R.id.bitmovinPlayerView);
player = Player.create(context, playerConfig);
playerView.setPlayer(player);
player.on(SourceEvent.Loaded.class, this::onLoad);
player.on(PlayerEvent.Playing.class, this::onPlay);
player.on(PlayerEvent.Paused.class, this::onPause);
player.on(PlayerEvent.Seek.class, this::onSeek);
player.on(PlayerEvent.TimeChanged.class, this::onTimeChanged);
player.on(PlayerEvent.Destroy.class, this::onDestroy);
player.on(PlayerEvent.Seeked.class, this::onSeeked);
player.on(PlayerEvent.PlaybackFinished.class, this::onPlaybackFinished);
player.on(PlayerEvent.Ready.class, this::onReady);
player.on(SourceEvent.Error.class, this::onError);
player.on(SourceEvent.SubtitleChanged.class, this::onSubtitleChanged);
player.on(PlayerEvent.Error.class, this::onError);
subtitleView = findViewById(R.id.bitmovinSubtitleView);
subtitleView.setPlayer(player);
player.setVolume(100);
}
I have read that React Native styles the top-level view of a UI Component, so this is my only clue at this stage. I'm unsure how to respond to that info however...
EDIT: The problem is likely to be that dynamically updating view layouts in Android in React Native is not straightforward. This has been discussed at length here.
EDIT 2: I have tried to listen for global layout changes, which is one of the proposed workarounds for view layout issues:
getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
requestLayout();
}
});
This is called as expected, but has no effect on the subtitleView, which still displays at the top of the player, which seems to be because it has a height of 0.
EDIT 3: Another suggested solution that didn't work for me:
private void setupLayoutHack() {
Choreographer.getInstance().postFrameCallback(new Choreographer.FrameCallback() {
#Override
public void doFrame(long frameTimeNanos) {
manuallyLayoutChildren();
getViewTreeObserver().dispatchOnGlobalLayout();
Choreographer.getInstance().postFrameCallback(this);
}
});
}
private void manuallyLayoutChildren() {
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
child.measure(MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY));
child.layout(0, 0, child.getMeasuredWidth(), child.getMeasuredHeight());
}
}
I called setupLayoutHack() in the constructor but saw no difference after applying those changes either :(
EDIT 4: My final attempt at fixing the SubtitleView layout was experimenting with measuring and laying out in various ways:
private void refreshViewChildrenLayout(View view){
view.measure(
View.MeasureSpec.makeMeasureSpec(view.getMeasuredWidth(), View.MeasureSpec.EXACTLY),
View.MeasureSpec.makeMeasureSpec(view.getMeasuredHeight(), View.MeasureSpec.EXACTLY));
view.layout(view.getLeft(), view.getTop(), view.getRight(), view.getBottom());
}
However, the height in all cases that I tried was 0, which meant nothing was altered. There is a solution mentioned in the above RN issue that suggests that the shadow node for the subtitle view should be overridden. So one way forward could be to build a new subtitle view that has that included.
However, at this stage it seems to me an easier approach to respond to subtitle cues in React Native and perform all display and styling there.
(There is also a lesser issue of how to make the background on either side of the text transparent, but the layout issue is far more important at this stage).
Disclaimer: I'm not very familiar with React Native and how it influences layout creation if at all.
However looking at your layout file, it indicates that the SubtitleView is the top child of the PlayerView, which is a FrameLayout, thus gets added at the top (left). By specifying android:layout_height="wrap_content" on the SubtitleView it will only take up space that is required by the view. In the Bitmovin sample, it is generated in code and therefore should inherit the attributes from the parent, which is a RelativeLayout with android:layout_weight="1" which results in stretching it's height to the space available.
Long story short, try setting the height of your SubtitleView to match_parent
I am using an ImageView as a NEXT button in my Android app which is responsible for loading the next page of results into the current activity. However, despite that I bind a click listener to it, I cannot seem to capture click events on this ImageView. Here is the XML:
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="50"
android:gravity="left"
android:orientation="horizontal">
<ImageView
android:id="#+id/listBackIcon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/back_icon"/>
<TextView
android:id="#+id/listBackLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Prev"
android:textSize="16dip"/>
</LinearLayout>
And here is the relevant Java code:
ImageView forwardIconView = (ImageView)findViewById(R.id.listBackIcon);
// not sure if necessary; doesn't fix it anyway
forwardIconView.setClickable(true);
forwardIconView.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
++pageNumber;
try {
params.put("page", pageNumber);
} catch (JSONException e) {
// do something
}
ConnectionTask task = new ConnectionTask();
task.execute(new String[0]);
}
});
I spent about an hour researching this on Stack Overflow. I found a few places which claimed that ImageView could directly be made clickable, but most things recommended workarounds using other types of widgets.
Does anything about my layout/code stand out as being a culprit for this behavior?
Update:
I also tried binding a click listener to the TextView at the same level as the ImageView and this too failed to capture clicks. So now I am suspecting that the views are being masked by something. Perhaps something is capturing the clicks instead of the views.
I would set it up like this:
private ImageView nextButton;
nextButton = (ImageView)view.findViewById(R.id.back_button);
Util.loadImage(getActivity(),R.drawable.some_image,nextButton); //Here i would load the image, but i see you do it in XML.
nextButton.setOnClickListener(nextButtonListener);
nextButton.setEnabled(true);
View.OnClickListener nextButtonListener = new View.OnClickListener() {
#Override
public void onClick(View view) {
Log.v(TAG, "ImageView has been clicked. do something.");
}
};
This works for me.
Why not use android:drawableLeft attribute for the textview instead of using imageView​ and textview both in a linearlayout .
<ImageView
android:id="#+id/listBackIcon"
...
android:clickable="true"
Or you can try overriding onTouchListener with ACTION_DOWN event filter, not onClickListener. Also check for parrents with android:clickable="false", they could block childs for click events.
What seemed to work for me was the accepted answer from this SO question, which suggests adding the following the every child element of the LinearLayout which I pasted in my question:
android:duplicateParentState="true"
I don't know exactly what was happening, but it appears the click events were not making it down to the TextView and ImageView. Strangely, the click events were reaching a Button, when I added one for debugging purposes. If someone has more insight into what actually happened, leave a comment and this answer can be updated.
I am developing an Android offline mapping application using osmdroid and osm bonus pack and loading the tiles and data from external storage. Right now, as the data grows, markers are starting to get cramped together, I even have the situation of two places on the same building. I know this kind of issue has been asked a lot before, mine is about a simple temporal workaround I'm thinking of implementing. How about if two places are near enough(right in top of each other!) the standard info window pops up as a ListView with each row designed like the standard bubble(image, title, moreInfoButton).
My question is: some thoughts or advices on how to create the new bonuspack_bubble.xml layout file.
I don't know if this will help you.
I needed to create a CustomInfoBubble for my project. What I did was, to extend the InfoWindow default class, and pass to it my custom bubble layout. Something like this:
http://mobiledevstories.wordpress.com/2014/03/01/osmdroid-bonus-pack-markers-with-clickable-infowindows/
My Java class MapCustomInfoBubble looks like this:
public class MapCustomInfoBubble extends InfoWindow {
public MapCustomInfoBubble(MapView mapView) {
super(R.layout.map_infobubble_black, mapView);//my custom layout and my mapView
}
#Override
public void onClose() {
//by default, do nothing
}
#Override
public void onOpen(Object item) {
Marker marker = (Marker)item; //the marker on which you click to open the bubble
String title = marker.getTitle();
if (title == null)
title = "";
Button moreInfo = (Button)mView.findViewById(R.id.bubble_moreinfo);//the button that I have in my XML;
moreInfo.setText(title);
moreInfo.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
gotoMoreInfoWindow();//custom method; starts another activity
}
});
}
}
In my XML file, I have:
<?xml version="1.0" encoding="UTF-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:background="#drawable/map_infobubble_black" >
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<TextView android:id="#+id/bubble_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#FFFFFF"
android:maxEms="17"
android:layout_gravity="left"
android:layout_weight="1"
android:text="Title" />
<Button android:id="#+id/bubble_moreinfo"
android:background="#drawable/map_btn_moreinfo"
android:visibility="visible"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:layout_weight="0" />
</LinearLayout>
</LinearLayout>
Somewhere else in my code, I then use:
Marker wp = new Marker(mapView);
wp.setPosition(new GeoPoint(mylocation.getMyLocation()));
wp.setTitle(editTextName.getText().toString());
wp.setInfoWindow(new MapCustomInfoBubble(mapView));
mapView.getOverlays().add(wp);
mapView.invalidate();
In my code, I set the text on the button with the Marker's title.
The Marker is the item on which I click. If you want to put info about more markers in the same InfoWindow (inside a ListView), I think you would need to know in advance what the info will be.
I believe that, You can put whatever code you want inside onOpen(), however, I am not so sure if it's a good practice. You could try creating a custom Constructor and put your logic there. It should work.
You need to pass the Resource Id (layout) and mapView to the super constructor, so it returns a valid mView object.
I am using AsyncTask in my application to load data from Internet. Here is onPostExecute method of my class that extends Async class.
protected void onPostExecute(Article result) {
super.onPostExecute(result);
TextView txtTitle= (TextView) view.findViewById(R.id.title);
txtTitle.setText(result.getTitle());
TextView txtMain= (TextView) view.findViewById(R.id.main);
txtMain.setText(result.getContent());
}
This is the overridden method from PageAdapter that instantiate pages. customlayout is the layout that shows article.
public Object instantiateItem(View container, int position) {
LayoutInflater inflater = (LayoutInflater) container.getContext()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View page= inflater.inflate(R.layout.customlayout , null);
Worker aw = new Worker(page, container.getContext());
aw.execute(links.get(position));
((ViewPager) container).addView(page,0);
return page;
}
Here is layout of a page that is inflated in each page.
<TextView
android:id="#+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
<TextView
android:id="#+id/main"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="20sp" />
What I am trying to achieve is show a progress information until data loads. I should be able to swipe through page even while loading.
I would be grateful if anyone would help.
Also how can I control number of pages that are being loaded in background?
If you are looking for a spinning progress bar, you can try this:
xml:
<FrameLayout android:id="#+id/progressContainer">
<ProgressBar style="#android:style/Widget.ProgressBar.Large"
android:layout_gravity="center" />
</FrameLayout>
activity:
private View progressContainer;
#Override
public View onCreateView(...) {
progressContainer = v.findeViewById(R.id.progressContainer);
progressContainer.setVisibility(View.INVISIBLE);
}
So whenever you need to display the Progress just set the View to visible.
If you want to be able to still interact with your activity, you can just modify the clickability of the parent FrameLayout:
android:clickable="true" (or "false")
Set it to false if you want to interact, and true to block!
I recommend to use fragments as your viewPager items, and implement the loading pattern inside the fragment i.e - https://github.com/johnkil/Android-ProgressFragment
Regarding number of pages being loaded see - setOffscreenPageLimit
If I am not wrong. You are creating something like youtube video, fetching the frame on background and keep update it. In your case is book, fetching pages on background and keep update on it.
First, What you needs is fragment as you cannot destroy your AsyncTask that running on your activity.
Second, AsyncTask publishProgress and onProgressUpdate(Progress...) method, it help you to report background condition. You can take a look at the usage example provided by android
http://developer.android.com/reference/android/os/AsyncTask.html
I'm trying to set up a "close to start" button in a game. The game takes the user from view to view like a maze. The user could be on any view, but I want a way to get them back to the start screen. Is there a way to find and return the ID of the current view for my findViewByID? I've found a I've tried the following code in various forms:
OnClickListener closetomain = new OnClickListener() {
#Override
public void onClick(View v) {
int currentid = v.findFocus().getId();
RelativeLayout hideme = (RelativeLayout)findViewById(currentid);
hideme.setVisibility(View.GONE);
setContentView(R.layout.main);
// RelativeLayout showme = (RelativeLayout)findViewById(R.id.startLayout);
// showme.setVisibility(View.VISIBLE);
}
};
Okay, it turns out I should have given each close button the same ID and used theisenp's suggestion for the simplest code (well, that I could understand). It also turns out that I should have started by putting each level/layout in its own activity. You live and you learn I guess. Below is my XML and java. It may not be the elegant solution but I'm not sure I care all that much as long as it works.
<ImageButton android:id="#+id/closeButton"
android:layout_height="wrap_content"
android:background="#drawable/close"
android:layout_width="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginLeft="10dp"
android:layout_marginBottom="10dp"
android:onClick="closetomain">
</ImageButton>
And here's my Java:
public void closetomain(View view) {
switch(view.getId()) {
case R.id.closeButton:
setContentView(R.layout.main);
break;
}
}
Why do you need to retrieve the id of the current view? Is it just so that you can set it's visibility to GONE? If so, there are probably better ways of implementing that same functionality.
Instead of just changing the layout with setContentView(), it would probably be a better idea to have the Start Screen be its own separate activity. When you are in any of the "maze views" you could simply use an intent to start your home screen activity like this
Intent startScreenIntent = new Intent(this, StartScreen.Class);
startActivity(startScreenIntent);
Then you won't have to worry about finding id's or changing visibilities, plus the code is cleaner, because it separates the code for your Maze levels and your Start Menu.