How do i export a subview of a fragment from another fragment as Png?
Context
I'am creating an app, which allows the user to create a personalized CV. The user can submit informations regarding his job-experience and skills. As a result the user is able to export his results as a png and save it to the device. My question aims at the export-functionallity of the application.
What i got so far
I tried to combine several answers from the site to get a result, but unfortunately the code that i have does not work so far.
public void export(Context context) throws FileNotFoundException {
View exportView = getLayoutInflater(getArguments()).inflate(R.layout.fragment_form, null, false);
RelativeLayout subView = (RelativeLayout) exportView.findViewById(R.id.fragment_form_container_root);
try {
subView.setDrawingCacheEnabled(true);
subView.measure(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
Bitmap b = Bitmap.createBitmap(subView.getDrawingCache(), 0, 0, subView.getMeasuredWidth(), subView.getMeasuredHeight());
File cachePath = new File(context.getCacheDir(), "images");
cachePath.mkdirs(); // don't forget to make the directory
FileOutputStream stream = new FileOutputStream(cachePath + "/image.png"); // overwrites this image every time
b.compress(Bitmap.CompressFormat.PNG, 100, stream);
stream.close();
} catch (Exception e) {
e.printStackTrace();
}
File imagePath = new File(context.getCacheDir(), "images");
File newFile = new File(imagePath, "image.png");
Uri contentUri = FileProvider.getUriForFile(context, "com.example.lukas.masterthesis.fileprovider", newFile);
if (contentUri != null) {
Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND);
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); // temp permission for receiving app to read this file
shareIntent.setDataAndType(contentUri, getActivity().getContentResolver().getType(contentUri));
shareIntent.putExtra(Intent.EXTRA_STREAM, contentUri);
startActivity(Intent.createChooser(shareIntent, "Choose an app"));
}
}
The export functionallity is called within Fragment A after a click event. I'am trying to get the wanted subview of Fragment B (wich seems to work), but creating the Bitmap always results in a null Object. Therefore the procedure fails.
java.lang.NullPointerException: Attempt to invoke virtual method 'boolean android.graphics.Bitmap.compress(android.graphics.Bitmap$CompressFormat, int, java.io.OutputStream)' on a null
Honestly im not even sure this is the proper way to do such a thing, since I haven't found a Best-Practice solution for what im trying. If not, does anybody know a better way to do this?
If its an "ok" solution, does anybody know how to get my code to work or has some suggestions that can point me into the right direction?
Thx in advance.
try using
try using bitmap = Bitmap.createBitmap(subView.getDrawingCache());
instead of
Bitmap b = subView.getDrawingCache();
Just wanted to provide an answer.
I was basically on the right track. Ended up splitting my View into smaller parts since may drawing cache was to small to handle the whole thing at once. It might help somebody who has encountered a similar problem.
First part of code draws each part into the resulting bitmap. Second part allows the user to exports the it (for example to dropbox).
So here you go
public void export() throws FileNotFoundException {
View exportView = rootView.findViewById(R.id.fragment_form_container_root);
View view_title = rootView.findViewById(R.id.fragment_form_container_title);
View view_basicInformation = rootView.findViewById(R.id.fragment_form_container_basicinformation);
View view_experience_skill = rootView.findViewById(R.id.fragment_form_container_experience_skill);
View view_connect = rootView.findViewById(R.id.fragment_form_container_connect);
View view_footer = rootView.findViewById(R.id.fragment_form_container_footer);
try {
view_title.setDrawingCacheEnabled(true);
view_title.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH);
Bitmap b0 = Bitmap.createBitmap(view_title.getDrawingCache());
view_title.setDrawingCacheEnabled(false);
view_basicInformation.setDrawingCacheEnabled(true);
view_basicInformation.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH);
Bitmap b1 = Bitmap.createBitmap(view_basicInformation.getDrawingCache());
view_basicInformation.setDrawingCacheEnabled(false);
view_experience_skill.setDrawingCacheEnabled(true);
view_experience_skill.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH);
Bitmap b2 = Bitmap.createBitmap(view_experience_skill.getDrawingCache());
view_experience_skill.setDrawingCacheEnabled(false);
view_connect.setDrawingCacheEnabled(true);
view_connect.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH);
Bitmap b3 = Bitmap.createBitmap(view_connect.getDrawingCache());
view_connect.setDrawingCacheEnabled(false);
view_footer.setDrawingCacheEnabled(true);
view_footer.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH);
Bitmap b4 = Bitmap.createBitmap(view_footer.getDrawingCache());
view_footer.setDrawingCacheEnabled(false);
Bitmap result = Bitmap.createBitmap(exportView.getWidth(), exportView.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(result);
//canvas.setDensity(300);
Paint paint = new Paint();
paint.setFlags(Paint.FILTER_BITMAP_FLAG);
paint.setFlags(Paint.ANTI_ALIAS_FLAG);
canvas.drawBitmap(b0, 0, 0, paint);
canvas.drawBitmap(b1, 0, b0.getHeight(), paint);
canvas.drawBitmap(b2, 0, b0.getHeight() + b1.getHeight(), paint);
canvas.drawBitmap(b3, 0, b0.getHeight() + b1.getHeight() + b2.getHeight(), paint);
canvas.drawBitmap(b4, 0, b0.getHeight() + b1.getHeight() + b2.getHeight() + b3.getHeight(), paint);
File cachePath = new File(string_CachePath, "images"); // -> string_CachePath == context.getCacheDir();
cachePath.mkdirs(); // don't forget to make the directory
FileOutputStream stream = new FileOutputStream(cachePath + "/image.png"); // overwrites this image every time
result.compress(Bitmap.CompressFormat.PNG, 100, stream);
stream.close();
File newFile = new File(cachePath.getPath(), "image.png");
Uri contentUri = FileProvider.getUriForFile(getActivity(), "com.example.lukas.masterthesis.fileprovider", newFile);
if (contentUri != null) {
Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND);
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); // temp permission for receiving app to read this file
shareIntent.setDataAndType(contentUri, getActivity().getContentResolver().getType(contentUri));
shareIntent.putExtra(Intent.EXTRA_STREAM, contentUri);
startActivity(Intent.createChooser(shareIntent, "Choose an app"));
}
} catch (Exception e) {
e.printStackTrace();
}
}
Related
I need some help here.
I want to add a new feature to my app. I have a page that displays some text and I have a share button with it. Now the user can share this text with anyone, So what I want is that the user can share this text as an Image, So Instead of sharing this just like a text, I will add this new feature to also can share it as an Image.
Now I already created the Image that will include this text to be shared,
What I need to do is, When the user clicks on the share button, it is should send this text to the Image a Put it into it "It will be empty and the text will be like drawing on it. And after the text adds to the Image, The user can send this image with the text.
I made some searches on Google and I found some people who say you can add Textview into the ImageView and send this text with Intent then display the text in the Textview and then share it. But of course, this will not work because the text didn't actually add to the Image and It is just a view, so the Image will share as it is "Empty".
Any suggestions for how can I make something like that?
Wrap your image and text into a relative layout or constraint layout or a frame layout, take a screenshot of that relative or constraint or frame layout, and share it, use This Library to take a screen shot of your view.
here is some code for you
Bitmap bitmap = ScreenShott.getInstance().takeScreenShotOfJustView(yourview);
File sharedFile = FileUtility.shareImageFile(bitmap);
if (sharedFile != null) {
Uri uri = FileProvider.getUriForFile(context, context.getPackageName().concat(".provider"), sharedFile);
Intent intent = new ShareCompat.IntentBuilder(context)
.setType(context.getContentResolver().getType(uri))
.setStream(uri)
.getIntent();
intent.setAction(Intent.ACTION_SEND);
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(Intent.createChooser(intent, "share"));
}
FileUtility shareImageFile method
public static File shareImageFile(Bitmap bitmap) {
String rootDirectory = ResourceProvider.get().getContext().getExternalCacheDir() + File.separator + Environment.DIRECTORY_PICTURES + File.separator;
File rootFile = new File(rootDirectory);
if (!rootFile.exists()) {
boolean make = rootFile.mkdirs();
Log.d(TAG, "shareImageFileMakeStatus: " + make);
}
String imagePath = rootDirectory.concat("image_").concat(currentDate()).concat(".png");
try {
FileOutputStream fos = new FileOutputStream(imagePath);
bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
fos.flush();
fos.close();
return new File(imagePath);
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
I use external cashe dir because there is no need to get storage permission from user for this path.
file provider that you must add xml folder in your res and create a .xml file for file provider
res/xml/provide_paths.xml
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path
name="external_files"
path="." />
</paths>
add this provider in your manifest.xml
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/provider_paths" />
</provider>
You can make use of DrawingCache. Put your image and text inside a wrapper like FrameLayout/RelativeLayout/etc and use its drawing cache to save it as a bitmap;
View view = findViewById(R.id.flWrapper);
view.setDrawingCacheEnabled(true);
Bitmap bitmap = Bitmap.createBitmap(flUser.getDrawingCache());
view.setDrawingCacheEnabled(false);
// if you want to save it to a file
File image = new File("/path/to/your/file.jpg");
try (FileOutputStream outputStream = new FileOutputStream(image)) {
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);
outputStream.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
// handle it
} catch (IOException e) {
e.printStackTrace();
// handle it
}
// or share the bitmap as you like
There are 3 steps to this .
Create the view you want to send
Convert the view to Bitmap
Send it via an intent after saving it in your app or use it for your imageview
Create the view you wanto to send
So create a constraint layout which has that textview which is populated by the text you want to give it from the previous activity via a `intent.getExtra("text")
Have the images and other data in the *same constraint layout which you want to send. Once yo're satisfied with how the view/viewgroup aka the constraint layout looks. we are ready to convert that to bitmap
2.Convert the viewgroup to Bitmap
v.measure(MeasureSpec.makeMeasureSpec(v.getLayoutParams().width, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(v.getLayoutParams().height, MeasureSpec.EXACTLY));
v.layout(0, 0, v.getMeasuredWidth(), v.getMeasuredHeight());
Bitmap b = Bitmap.createBitmap(v.getWidth(), v.getHeight(), Bitmap.Config.RGB_565);
Canvas c = new Canvas(b);
v.draw(c);
where v is the viewgroup you want to convert to bitmap
Viewgroup to Bitmap Stackoverflow
3.Send that bitmap as an intent
String pathofBmp = Images.Media.insertImage(getContentResolver(), bitmap,"title", null);
Uri bmpUri = Uri.parse(pathofBmp);
final Intent emailIntent1 = new Intent( android.content.Intent.ACTION_SEND);
emailIntent1.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
emailIntent1.putExtra(Intent.EXTRA_STREAM, bmpUri);
emailIntent1.setType("image/png");
and for imageview it's as simple as
imageview.setSrc(bitmap)
Share Bitmap via Android Stackoverllow answer
I am making a photo app in which in the first activity the user takes a picture (he can see the picture in the ImageView), in the second activity he chooses with who to share the image,and in the 3rd activity he should be able to see the image again in a different ImageView than the first to add some data. I know how to move the bitmap from one activity to the next one by an intent, but how to do it if i want to send it to the 3rd activity of my user path? If i startActivity(intent) it will skip my second activity and if i don´t put it the 3rd activity is showing me an empty ImageView.. Can someone please help me in telling me ways of how to automatically load (without user interaction) this picture in the 1st and 3rd activity and some example?
I already being reading posts about how to convert to Base64 and load again, but their examples are using images already in the memory of the phone and in my case are pictures that were just taken by the user, so in principle i don´t know the name of the image file..
Thank a lot!
Add This Image In Your Custome Catch Folder
Like Make Your Folder in External or Internal Storage
Then Save Image that will capture by camera inside That Folder..
public static void SaveImagecatch(Bitmap finalBitmap) throws IOException {
File Folder = new File(Environment.getExternalStorageDirectory() + "/data/Catch");
if (Folder.mkdir()) {
nomediaFile = new File(Environment.getExternalStorageDirectory() + "/data/Catch/" + NOMEDIA);
if (!nomediaFile.exists()) {
nomediaFile.createNewFile();
}
}
String root = Environment.getExternalStorageDirectory().toString();
File myDir = new File(root + "/data/Catch");
myDir.mkdirs();
Random generator = new Random();
int n = 10000;
n = generator.nextInt(n);
String fname = "Image-" + n + ".jpg";
File file = new File(myDir, fname);
Catch_uri = Uri.parse("file://" + myDir + "/" + fname);
if (file.exists()) file.delete();
try {
FileOutputStream out = new FileOutputStream(file);
finalBitmap.compress(Bitmap.CompressFormat.JPEG, 100, out);
out.flush();
out.close();
Log.e("yes", "yes");
} catch (Exception e) {
e.printStackTrace();
Log.e("no", "no");
}
}
Then.. get Image From Uri path of Your saved Image.
Uri imageUri = Catch_uri;
Bitmap bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(),imageUri);
Imageview my_img_view = (Imageview ) findViewById (R.id.my_img_view);
my_img_view.setImageBitmap(bitmap);
This is Worked For Me.. I Hope This Will be Helpfull to you
Actually in second activity, you need to get Intent from first activity and do your work and then create new Intent and put your image into it, finally start third activity using new intent.
in second activity:
Intent firstToSecodeIntent = getIntent();
// some codes
Intent secondToThirdIntent = new Intent(this, ThirdActivity.class);
Intent.putExtra("image", /*your Image object*/);
startActivity(secondToThirdIntent);
in third activity:
Intent secondToThirdIntent = getIntent();
// get your image and set it into your imageView
This sounds like an easy task, but I can't upload a bitmap, I took with camera, into my app. Well, technically I can, but nothing get's displayed.
I use this code for taking pictures on onClick:
count++;
file = dir+count+".jpg";
File newfile = new File(file);
try {
newfile.createNewFile();
} catch (IOException e) {}
Uri outputFileUri = Uri.fromFile(newfile);
Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, outputFileUri);
startActivityForResult(cameraIntent, TAKE_PHOTO_CODE);
Where
dir - final String dir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
And this code for image loading:
String location = new String(Environment.getExternalStorageDirectory().getAbsolutePath() + "/Pictures/" + count + ".jpg");
Bitmap myBitmap = BitmapFactory.decodeFile(location);
Bitmap mutableBitmap = myBitmap.copy(Bitmap.Config.ARGB_8888, true);
Canvas canvas = new Canvas(mutableBitmap);
wv2.draw(canvas);
tstImage.setImageBitmap(mutableBitmap);
text.setText(Float.toString(myBitmap.getHeight()) + " " + Float.toString(myBitmap.getWidth()) + " px");
Where
count - Integer that represents name of the image
wv2 - custom WebView in which I draw canvas.
I do not think that these lines are relevant, but doesn't hurt to post additional info - you never know.
Canvas canvas = new Canvas(mutableBitmap);
wv2.draw(canvas);
This line displays text with dimensions of the image. I use it to check whether I load the bitmap.
text.setText(Float.toString(myBitmap.getHeight()) + " " + Float.toString(myBitmap.getWidth()) + " px");
When I run this code image does not get displayed, but text shows correct dimensions, which should mean that I do load the bitmap. What am I doing wrong? Where is the problem?
[UPDATE]
I think I'm making progress. Now I get outOfMemory problem and the app crashes.
String path = Environment.getExternalStorageDirectory()+ "/Pictures/" + count + ".jpg";
File imgFile = new File(path);
if(imgFile.exists()){
Bitmap mBitmap = BitmapFactory.decodeFile(imgFile.getAbsolutePath());
tstImage.setImageBitmap(mBitmap);
} else {
Log.d("Ciaren", "File doesn't exist");
}
OOM indicates that the image is way too big for memory to handle. There are two solutions here:
Either force your way through with largeHeap="true" in the manifest, which is okay, but can lead to problems in the future.
Or you can change the options of bitmap loading, so it takes less memory at a time. Thus the image will take more time to load, but remove the possibility of the crash. Plus, the time difference is barely noticable.
Instead of using bitmap image get the uri or path and use picasso to load that image into imageView
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode != Activity.RESULT_OK) {
return;
}
switch(requestcode){
case MEDIA_TYPE_IMAGE:
Uri selectedImageUri = data.getData();
Bitmap bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), seletedImageUri);
Picasso.with(getContext()).load(selectedImageUri).fit().into(imgDp);
break;
}
}
i spand few hours to find this solution...
so i decided to share this informatiom, maybe some one itwill helpful :)
The first way, shown below, takes the bitmap from the view and loads it into a file.
// Get access to ImageView
ImageView ivImage = (ImageView) findViewById(R.id.ivResult);
// Fire async request to load image
Picasso.with(context).load(imageUrl).into(ivImage);
and then later assuming after the image has completed loading, this is how you can trigger a share:
// Can be triggered by a view event such as a button press
public void onShareItem(View v) {
// Get access to bitmap image from view
ImageView ivImage = (ImageView) findViewById(R.id.ivResult);
// Get access to the URI for the bitmap
Uri bmpUri = getLocalBitmapUri(ivImage);
if (bmpUri != null) {
// Construct a ShareIntent with link to image
Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND);
shareIntent.putExtra(Intent.EXTRA_STREAM, bmpUri);
shareIntent.setType("image/*");
// Launch sharing dialog for image
startActivity(Intent.createChooser(shareIntent, "Share Image"));
} else {
// ...sharing failed, handle error
}
}
// Returns the URI path to the Bitmap displayed in specified ImageView
public Uri getLocalBitmapUri(ImageView imageView) {
// Extract Bitmap from ImageView drawable
Drawable drawable = imageView.getDrawable();
Bitmap bmp = null;
if (drawable instanceof BitmapDrawable){
bmp = ((BitmapDrawable) imageView.getDrawable()).getBitmap();
} else {
return null;
}
// Store image to default external storage directory
Uri bmpUri = null;
try {
File file = new File(Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_DOWNLOADS), "share_image_" + System.currentTimeMillis() + ".png");
file.getParentFile().mkdirs();
FileOutputStream out = new FileOutputStream(file);
bmp.compress(Bitmap.CompressFormat.PNG, 90, out);
out.close();
bmpUri = Uri.fromFile(file);
} catch (IOException e) {
e.printStackTrace();
}
return bmpUri;
}
Make sure to add the appropriate permissions to your AndroidManifest.xml:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
The second way to share an Image does not require you to write the image into a file. This code can safely be executed on the UI thread. The approach was suggested on this webpage http://www.nurne.com/2012/07/android-how-to-attach-image-file-from.html .
ImageView siv = (ImageView) findViewById(R.id.ivResult);
Drawable mDrawable = siv.getDrawable();
Bitmap mBitmap = ((BitmapDrawable)mDrawable).getBitmap();
String path = Images.Media.insertImage(getContentResolver(),
mBitmap, "Image Description", null);
Uri uri = Uri.parse(path);
return uri;
You get the Drawable from the ImageView. You get the Bitmap from the Drawable. Put that bitmap into the Media image store. That gives you a path which can be used instead of a file path or URL. Note the original webpage had an additional problem with immutable bitmaps, solved by drawing the bitmap into a canvas (never shown on screen). See linked page above for details.
I have found lots of solution to change photo orientation for DISPLAY, and succeeded. But now I need to upload that photo by using file.path. Is there anyway to directly change the photo orientation but not just display differently?
CustomMultiPartEntity multipartContent = new CustomMultiPartEntity(
new CustomMultiPartEntity.ProgressListener() {
#Override
public void transferred(long num) {
Constant.Process = (int) ((num / (float) totalSize11) * 100);}
});
multipartContent.addPart("key", new StringBody(Constant.Key));
multipartContent.addPart("userfile", new FileBody(new File(up.FilePath)));
httpPost.setEntity(multipartContent);
HttpResponse response1 = httpClient.execute(httpPost, httpContext);
response = EntityUtils.toString(response1.getEntity());
Something like that, I need to insert a FileBody by a FilePath into that 'multipartContent', then upload, is there anyway to upload a correct orientation photo??
Thank you very much!
EDIT: This is my onclick method to have camera, how can I add code to rotate image before it was saved?
public void onClick(View v) {
if (v == btn_camera) {
Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
Date date = new Date();
DateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");
String newPicFile = "Miuzz"+ df.format(date) + ".jpg";
String outPath = "/sdcard/" + newPicFile;
File outFile = new File(outPath);
mUri = Uri.fromFile(outFile);
cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, mUri);
startActivityForResult(cameraIntent, 1);
}
}
There is unfortunately no way of modifying the orientation of the photo file other other than to load the image, rotate it manually and re-save it in it's correct orientation.
See this question for some details on how to save the image once it's rotated: Android Rotate Picture before saving
Edit: Ok, so what you'll want to do is in your onActivityResult() method, load the image and rotate it as you already do. After rotating it, you should have a correctly orientated Bitmap object, then all you need to do is either overwrite your existing photo, or create a new file like this:
try {
FileOutputStream out = new FileOutputStream(filename);
bitmap.compress(Bitmap.CompressFormat.PNG, 90, out);
out.close();
} catch (Exception e) {
e.printStackTrace();
}
Also worth noting, is that if you decide to create a new file, be sure to delete it after your upload is complete otherwise you'll be unnecessarily filling up the user's internal storage.