I'm currently developing an application on Android with AndroidPDFViewer : https://github.com/barteksc/AndroidPdfViewer
I want to create a functionality, when the user touch the screen, it put a point on the PDF at this location. After I want to measure the distance between 2 points but it's an other problem.
I don't understand how to do this functionality, put a point on PDF. I found this : https://github.com/barteksc/AndroidPdfViewer/issues/554
So it's possible but how ? I don't get it.
I suppose I need to create a bitmap, but I can't draw on the PDF, or put a marker.
Thanks for your time.
You have a sample file in his repo.
If you go to this location he has already created the method on how to load a pdf! Then if you go to this link you will see that he has created the class and has included good comments
Render page fragment on {#link Surface}
Page must be opened before rendering.
public void renderPage(PdfDocument doc, Surface surface, int pageIndex,
int startX, int startY, int drawSizeX, int drawSizeY) {
renderPage(doc, surface, pageIndex, startX, startY, drawSizeX, drawSizeY, false);
}
The one that you are searching is bookmark method
/** Get table of contents (bookmarks) for given document */
public List<PdfDocument.Bookmark> getTableOfContents(PdfDocument doc) {
synchronized (lock) {
List<PdfDocument.Bookmark> topLevel = new ArrayList<>();
Long first = nativeGetFirstChildBookmark(doc.mNativeDocPtr, null);
if (first != null) {
recursiveGetBookmark(topLevel, doc, first);
}
return topLevel;
}
}
However keep in mind that it might be necessary to use Async Tasks to download a pdf!
Related
I am writing an app that generates Maths worksheets for school students. It will, for example, generate 2 to 5 pages of simple Maths questions and 1 to 2 pages of answers. The PDF can be saved to file and loaded again later. Then it has a print function that can print all the pages. I want to make it skip printing the answer pages.
Is it possible to automatically identify which pages are the answer pages? I can only think of a workaround by making those answer pages have special height or width but not even sure if this works. Are there any better ways to do this?
Ok, I continued the project and used the following method: when constructing the PDF, I put the word "Answer on the top left corner with a gray rectangle surrounding it drawn with drawRect(). Then before the actual printing, I used the following code inside the PrintDocumentAdapter() class to check whether the color of the pixel 0,0 is gray or not.
#Override
public void onStart() {
if (parcelFileDescriptor != null) {
try {
pdfRenderer = new PdfRenderer(parcelFileDescriptor);
} catch (IOException e) {
e.printStackTrace();
}
}
int tempTotal = pdfRenderer.getPageCount();
Bitmap[] tempBitmap = new Bitmap[tempTotal];
finalTotal = tempTotal;
for (int pageNum = 0; pageNum < tempTotal; pageNum++) {
PdfRenderer.Page tempPage = pdfRenderer.openPage(pageNum);
tempBitmap[pageNum] = Bitmap.createBitmap(WS_WIDTH, WS_HEIGHT, Bitmap.Config.ARGB_8888);
tempPage.render(tempBitmap[pageNum], null, null, PdfRenderer.Page.RENDER_MODE_FOR_PRINT);
if (tempBitmap[pageNum].getPixel(0, 0) == Color.GRAY) {
finalTotal--;
}
tempPage.close();
}
}
It works fine. At least should cause no problem if the users only attempt to print PDF files constructed with my app. :P
Please tell me if you know a better way to do this. Thanks!
I am currently using the code here, albeit heavily modified to suit my needs
https://stackoverflow.com/a/28186621/4541217
such as I need to take an image from the camera as well as select from the gallery. I am also zooming the image.
This all works nicely, except for one issue. I lose things when I rotate the device.
I have
bTemp = null;
if(getLastNonConfigurationInstance() != null) {
bTemp = getLastNonConfigurationInstance();
}
in my onCreate, and an override...
#Override
#Deprecated
public Object onRetainNonConfigurationInstance() {
return bTemp;
}
I can make this return the image but I lose all of my stroke information.
From the example, I have tried saving the Uri, the alteredBitmap, the bitmap and the choosenImageView. However, none of these are working. If I take a photo, scribble on it, then before doing anything else, using the alteredBitmap, if I rotate, then I get the first set of strokes. However, nothing after that.
Can anyone help me to keep my stroke information on rotate please?
Learn about the activity lifecycle.
You need to override functions like onPause, onResume, and use the savedInstanceState.
I managed to work it out eventually, so for anyone else that is trying to do the same, here is what I did.
Following on from the example link in my opening post, in order to make it stick while rotating...
in the onRetainNonConfigurationInstance, keep the alteredBitmap. (This is in the Activity)
#Override
#Deprecated
public Object onRetainNonConfigurationInstance() {
return alteredBitmap;
}
then, in the onCreate of the activity...
if(getLastNonConfigurationInstance() != null) {
bmp = (Bitmap)getLastNonConfigurationInstance();
alteredBitmap = Bitmap.createBitmap(bmp.getWidth(), bmp.getHeight(), bmp.getConfig());
choosenImageView.setNewImage(alteredBitmap, bmp);
}
notice that the "bmp" is what was sent from alteredBitmap, and now alteredBitmap is the new image. This is then passed into the setNewImage in the DrawableImageView.
I am working on the OSMdroid library, and wanted to make it offline so i used this code
mMapView.setUseDataConnection(false);
but in this condition it won't even display the map other than the graph page. But when i change the boolean to true it again starts working.
So how do i make it work completely offline?
I am also trying to get the poi so i have the following code
geoPoint= new GeoPoint(37.439974,-119.003906);
double north = 84;
double east = -180;
double south = -84;
double west = 180;
boundingBoxE6 = new BoundingBoxE6(north, east, south, west);
mMapView.setScrollableAreaLimit(boundingBoxE6);
mMapView.setMultiTouchControls(true);
Marker marker= new Marker(mMapView,mResourceProxy);
marker.setPosition(geoPoint);
mMapView.getOverlays().add(marker);
poiMarkers = new FolderOverlay(getActivity());
new connection().execute();
mMapView.invalidate();
public class connection extends AsyncTask{
#Override
protected Object doInBackground(Object[] objects) {
NominatimPOIProvider poiProvider = new NominatimPOIProvider();
pois=poiProvider.getPOICloseTo(geoPoint,"atm",50,1000);
return pois;
}
#Override
protected void onPostExecute(Object o) {
super.onPostExecute(o);
mMapView.getOverlays().add(poiMarkers);
Drawable poiIcon = getResources().getDrawable(R.drawable.marker_poi_default);
if (o != null) {
for (POI poi : pois) {
Marker poiMarker = new Marker(mMapView);
poiMarker.setTitle(poi.mType);
poiMarker.setSnippet(poi.mDescription);
poiMarker.setPosition(poi.mLocation);
poiMarker.setIcon(poiIcon);
poiMarkers.add(poiMarker);
}
}else{
Toast.makeText(getActivity(),"null",Toast.LENGTH_SHORT).show();
}
}
}
But instead of getting the poi on the given geopint i get it somewhere else and everytime i mean whatever the code i change i don't get the poi in any other places other than that
http://nominatim.openstreetmap.org/search?format=json&q=[atm]&limit=50&bounded=1&viewbox=-1119.003906,1037.439974,880.996094,-962.560026
Is it possible to access poi in offline mode?
As for the offline tiles showing, are you sure you have the offline map tiles .ZIP-file stored in /mnt/sdcard/osmdroid/ (or whatever the path for your device's SDcard is, it just has to be within this folder in a subdirectory called osmdroid)?
A good tutorial that helped me can be found here: http://www.haakseth.com/?p=30
The OSMdroid TileSource will automatically look for this .ZIP-file if there is no internet connection to retrieve the tiles from. In the OpenStreetMapTileProviderConstants class there is this piece of code:
/** Base path for osmdroid files. Zip files are in this folder. */
public static final File OSMDROID_PATH = new File(Environment.getExternalStorageDirectory(),
"osmdroid");
So as you can see, you just need to have the osmdroid-directory in your SDcard folder, and everything should work just fine.
If you don't have a map tiles .ZIP yet, check out Mobile Atlas Creator for creating this.
So, my issue is this: I'm attempting to define a custom set of nodes for a Javafx XYChart LineChart, where each node corresponds to a point that was plotted directly from the datasets. After looking around a little bit, Jewlesea actually had a solution at one point about how to add dynamic labels to nodes on a linechart graph that gave me enough of a push in the right direction to create black symbols (they are dots at the moment, but they can be many different things). Now I have a requirement that requires me to change ONE of the nodes on the XY chart into an 'X'. this could be either through loading an image in place of the 'node', or through physically manipulating the 'shape' parameter in .css.
The problem begins when I try to add this property dynamically, since which node has the 'x' will always be changing. Here are the things I've tried, and they all end up with no results whatsoever, regardless of the property used.
private XYChart.Data datum( Double x, Double y )
{
final XYChart.Data data = new XYChart.Data(x, y);
data.setNode(
new HoveredThresholdNode(x, y));
//data.getNode().setStyle("-fx-background-image: url(\"redX.png\");");
data.getNode().styleProperty().bind(
new SimpleStringProperty("-fx-background-color: #0181e2;")
.concat("-fx-font-size: 20px;")
.concat("-fx-background-radius: 0;")
.concat("-fx-background-insets: 0;")
.concat("-fx-shape: \"M2,0 L5,4 L8,0 L10,0 L10,2 L6,5 L10,8 L10,10 L8,10 L5,6 L2,10 L0,10 L0,8 L4,5 L0,2 L0,0 Z\";")
);
data.getNode().toFront();
return data;
}
So in the above, you can see that this is adding a property through the use of the 'bind' function after the dataNode has already been created. Also note above, I tried doing it through the 'setStyle' interface at this level to give it a background image, with no success. Also, no errors are being thrown, no 'invalid css' or anything of the sort, just simply no display on the graph at all when done this way.
now, in the HoveredThresholdNode (Again a big thanks to Jewelsea for being a master of Javafx and putting this bit of code online, it's where 90% of this class came from.) I tried essentially the same thing, at a different level. (actually being IN the node creation class, as opposed to a layer above it).
class HoveredThresholdNode extends StackPane {
/**
*
* #param x the x value of our node (this gets passed around a bunch)
* #param y the y value of our node (also gets passed around a bunch)
*/
HoveredThresholdNode(Double x, Double y) {
//The preferred size of each node of the graph
//getStylesheets().add(getClass().getResource("style/XYChart.css").toExternalForm());
//getStyleClass().add("xyChart-Node");
//setOpacity(.8);
styleProperty().bind(
new SimpleStringProperty("-fx-background-color: #0181e2;")
.concat("-fx-font-size: 20px;")
.concat("-fx-background-radius: 0;")
.concat("-fx-background-insets: 0;")
.concat("-fx-shape: \"M2,0 L5,4 L8,0 L10,0 L10,2 L6,5 L10,8 L10,10 L8,10 L5,6 L2,10 L0,10 L0,8 L4,5 L0,2 L0,0 Z\";")
);
//this label is the 'tooltip' label for the graph.
final Label label = createDataThresholdLabel(x, y);
final double Myx = x;
final double Myy = y;
setOnMouseEntered(new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent mouseEvent) {
if (Myx == 0) {
label.setTextFill(Color.DARKGRAY);
} else if (Myx > 0) {
label.setTextFill(Color.SPRINGGREEN);
} else {
label.setTextFill(Color.FIREBRICK);
}
label.setText("Current position: " + Myx + " , " + Myy);
//setCursor(Cursor.NONE);
toFront();
}
});
setOnMouseExited(new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent mouseEvent) {
//getChildren().clear();
//setCursor(Cursor.CROSSHAIR);
}
});
}
Now note, I also tried the setStyle(java.lang.String) method, with all of the same type of CSS, with no success. I have NO idea why this isn't styling dynamically. It's almost as if the custom nodes are simply ignoring all new .css that I define at runtime?
Any help would be greatly appreciated, please don't be shy if you need more details or explanation on any points.
So, I did finally find a good workaround to solve my problem, although not in the way I thought it would happen. The main problem I was having, was that I was extending from stackPane to create my node, which only had a very small number of graphical display options available to it, and by switching the 'prefSize()' property, I was simply changing the size of that stackPane, and then filling in the background area of that stack pane black, giving it a very deceptive shape-look to it.
So rather than use a stack pane, whenever I reached the node that I needed to place the red 'X' on, I simply called a different Datum method that returned a datum with an ImageView Attached, like so:
private XYChart.Data CoLDatum(Double x, Double y){
final XYChart.Data data = new XYChart.Data(x, y);
ImageView myImage = new ImageView(new Image(getClass().getResource("style/redX.png").toExternalForm()));
data.setNode(myImage);
data.getNode().setOnMouseEntered(new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent mouseEvent) {
main_label.setText("Some Text.");
}
});
data.getNode().setOnMouseExited(new EventHandler<MouseEvent>(){
#Override public void handle(MouseEvent mouseEvent) {
main_label.setText("");
}
});
return data;
}
and since ImageView is an implementing class of Node, this worked out just fine, and allowed me to load up an image for that one single node in the graph, while still maintaining a listener to give custom text to our information label when the red 'x' was hovered over with a mouse. Sometimes, it's the simple solutions that slip right past you.
I imagine that, had I employed stackPane properties properly with the setStyle(java.lang.String) method, they would have absolutely shown up, and I was just butchering the nature of a stack pane. Interesting.
Hopefully this helps somebody else stuck with similar problems!
I have a ScrollView with a lot buttons. Each button is enabled when the user unlocks that button/level. I would like to focus the ScrollView on the latest unlocked button/level. See the screenshot below.
I found some functions like scrollTo() that could focus the ScrollView either at top, button or something like that but I would like to focus the ScrollView at certain places like the button ID that says Level 8 in the screenshot below. How would I go about doing this?
I think this should be good enough :
yourButtonView.getParent().requestChildFocus(yourButtonView,yourButtonView);
public void RequestChildFocus (View child, View focused)
child - The child of this ViewParent that wants focus. This view will contain the focused view. It is not necessarily the view that actually has focus.
focused - The view that is a descendant of child that actually has focus
First you need to know the position of item in scroll that has to get focus once you know that you can use following code to make that item focus
final int x;
final int y;
x = rowview[pos].getLeft();
y = rowView[pos].getTop();
yourScrollView.scrollTo(x, y);
refer this question
I agree with the benefits of previous answers. However, by experience, I have found this to be more complex than this. The setSelectionFromTop is very sensitive and often breaks if it is executed too early. This may depend on different reasons but the two primary reasons are that
If executed from within Activity lifecycle methods the views have not been loaded/configured yet.
View modifications triggered after the list move action seems to break the move. Probably overwriting some value before the move has been finalized due to a recalculation of the views.
The reasoning seems to apply for both setSelectionFromTop and setSelection() methods although
I tested mostly with setSelectionFromTop. smoothScrollToPosition() seems to be more robust, probably because it by definition changes the list position delayed whn doing the smooth scrolling.
Look at this example source code:
// Wee need to pospone the list move until all other view setup activities are finished
list.post(new Runnable(){
#override
public void run() {
list.setSelectionFromTop(selectedPosition, Math.max(0, Math.round(list.getHeight() / 3))); // Make sure selection is in middle of page, if possible.
// Make sure you do not modify (this or other) parts of the view afterwards - it may break the list move
// activityButtons.setVisibility(View.GONE);
}});
see: http://developer.android.com/reference/android/widget/ListView.html#setSelectionFromTop(int, int)
'int selectedLevel' holds the currently selected level, information is held in a Level class.
'values' is a list with all your levels, displayed by the list.
Example:
ListView lv = (ListView)findViewById(android.R.id.list);
int i = 0;
for (Level l : values) {
i++;
if (l.level == selectedLevel) {
lv.setSelectionFromTop(i, 200);
break;
}
}
try this
sc.post(new Runnable() {
public void run() {
sc.smoothScrollTo(x, y);
}
});
x the position where to scroll on the X axis
y the position where to scroll on the Y axis
Use this code, taken from this answer
private final void focusOnView(){
new Handler().post(new Runnable() {
#Override
public void run() {
your_scrollview.scrollTo(0, your_EditBox.getBottom());
}
});
}
Use this code, taken from my another answer
scrollViewSignup.post(new Runnable() {
#Override
public void run() {
int scrollY = scrollViewSignup.getScrollY();
scrollViewSignup.scrollTo(0, 0);
final Rect rect = new Rect(0, 0, view.getWidth(), view.getHeight());
view.requestRectangleOnScreen(rect, true);
int new_scrollY = scrollViewSignup.getScrollY();
scrollViewSignup.scrollTo(0, scrollY);
scrollViewSignup.smoothScrollTo(0, new_scrollY);
}
});
This code tries to smooth scroll and uses the standard behaviour of system to position to an item. You can simply change smoothScrollTo with scrollTo if you don't want the system to smooth scroll to the item. Or, you can use the code below only.
scrollViewSignup.post(new Runnable() {
#Override
public void run() {
scrollViewSignup.scrollTo(0, 0);
final Rect rect = new Rect(0, 0, view.getWidth(), view.getHeight());
view.requestRectangleOnScreen(rect, true);
}
});
Use the desired code block after trying, or, according to the need.