Gaps and unexpcted moves in RecyclerView - android-recyclerview

I'm using RecyclerView along with StaggeredGridLayoutManager for showing items with slightly different appearance. in items I use AspectRatioImageView by JakeWharton (link) to maintain the imageView sizes before the image actually downloaded and shown by Picasso. RV (RecyclerView) is populated by server data in infinite scroll pattern.
The problem is (as shown in included pictures), when scolling, some gaps appear on top and bottom of RV (in picture and sometimes items moves between the columns! (taking screenshot is hard :D)
Edit 2: A really important thing I forgot, is that I use the same layout for all of the items but regarding to adapter items some part of the layouts change their visibility to Gone or vice versa.
My question: Why RV behave like this? Am I doing something wrong? Is this normal in RV?
The code is large and I don't know which part is useful to put here. If you can narrow down the cause I will put some code here.
EDIT: Code parts related to problem
xml:
<android.support.v7.widget.RecyclerView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/recycler_view"
android:scrollbars="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/bg_texture"
android:padding="#dimen/padding_lvlhalf"
android:scrollbarStyle="outsideOverlay"/>
initializing view:
#Bind(R.id.recycler_view) RecyclerView mRecyclerView;
.
.
.
mRecyclerView.setLayoutManager(new StaggeredGridLayoutManager(columnCount, direction));
mRecyclerView.setAdapter(
new RecyclerViewAdapter<>(ctx, new ViewFiller<Post, DashboardPostViewHolder>() {
#Override
public void fill(Post post, DashboardPostViewHolder holder) {
holder.fill(post);
}
#Override
public DashboardPostViewHolder getViewHolder(View view) {
return new DashboardPostViewHolder(view, receiverName);
}
#Override
public int getLayoutID() {
return R.layout.post_dashboard;
}
});)
My custom Adapter:
public RecyclerViewAdapter(Context context, ViewFiller filler) {
super();
this.filler = filler;
mLayoutInflater = LayoutInflater.from(context);
items = new ArrayList<>();
}
#Override
public BaseViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
View v = mLayoutInflater.inflate(filler.getLayoutID(), parent, false);
return filler.getViewHolder(v);
}
#Override
public void onBindViewHolder(BaseViewHolder holder, int position) {
filler.fill(getItem(position), holder);
}
#Override
public int getItemCount() {
return items.size();
}
#Override
public long getItemId(int position) {
return position;
}
public T getItem(int position){
return items.get(position);
}
public void addItem(int position, T item){
items.add(position, item);
notifyItemInserted(position);
}
public void addItem(T item){
items.add(item);
notifyItemInserted(items.size() - 1);
}
public void addItems(List<T> data){
int oldSize = items.size();
items.addAll(data);
notifyItemRangeInserted(oldSize, data.size());
}
public void addItemsToListHead(List<T> data){
items.addAll(0, data);
notifyItemRangeInserted(0, data.size());
}
public void resetData(List<T> data) {
this.items = data;
notifyDataSetChanged();
}
public void removeItem(int position){
items.remove(position);
notifyItemRemoved(position);
}
public void clear(){
this.items.clear();
notifyDataSetChanged();
}
public List<T> getItems(){
return items;
}
ViewFiller interface
public interface ViewFiller<D, VH extends BaseViewHolder>{
void fill(D data, VH holder);
VH getViewHolder(View view);
#LayoutRes int getLayoutID();
}
Edit 2 (Cont.): My items xml and how I fill them..
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="#dimen/margin_lvlhalf"
app:cardUseCompatPadding="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:id="#+id/post"
android:background="#color/poinila_background">
<TextView
android:layout_gravity="right"
android:text="#string/test_title"
android:layout_margin="#dimen/margin_lvl1"
style="#style/match_wrap.large_text.centered_right"
android:id="#+id/post_title"
android:visibility="gone"/>
<!--Post Image-->
<com.shaya.poinila.android.presentation.view.AspectRatioImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scaleType="fitXY"
android:contentDescription="#string/post_content_description"
android:id="#+id/post_image"
android:visibility="gone"
app:aspectRatioEnabled="true"
app:dominantMeasurement="width"
/>
<!--Content-->
<TextView
android:id="#+id/post_content"
style="#style/match_wrap.medium_text.three_line.end_ellipsize"
android:layout_margin="#dimen/margin_lvl1"/>
<!--android:text="#string/test_long"-->
<include layout="#layout/horizontal_line"
/>
<!--Post Author-->
<include android:id="#+id/post_author"
layout="#layout/circle_image_title_subtitle_reverse"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="#dimen/margin_lvl1"/>
<include layout="#layout/horizontal_line"
/>
<!--Post Stats (favs, comments, etc) -->
<include android:id="#+id/post_stats"
layout="#layout/stats"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="#dimen/margin_lvlhalf"
android:layout_marginBottom="#dimen/margin_lvlhalf"/>
<include layout="#layout/horizontal_line"/>
<!--Post Collection Info-->
<include layout="#layout/rounded_image_title_subtitle_reverse"
android:id="#+id/post_collection"/>
</LinearLayout>
</android.support.v7.widget.CardView>
PostViewHolder class
public class PostViewHolder extends RecyclerView.ViewHolder
public void fill(Post post) {
if (post.type == PostType.IMAGE) {
postImage.setVisibility(View.VISIBLE);
postName.setVisibility(TextUtils.isEmpty(post.name) ? View.VISIBLE : View.GONE);
/*if (TextUtils.isEmpty(post.summary))
postContent.setVisibility(View.GONE);*/
}else{ //post.type == PostType.TEXT
postImage.setVisibility(View.GONE);
postName.setVisibility(View.VISIBLE);
postContent.setVisibility(View.VISIBLE);
}
// contetnt
if (TextUtils.isEmpty(post.summary) && !TextUtils.isEmpty(post.contentUrl)) {
DataRepository.getInstance().getPostContent(post.contentUrl, postContent);
}
else{
setText(postContent, post.summary);
}
.
.
.
}
}
Sorry for the long post!

You probably have margins in your root ViewGroup of item.
manager.setGapStrategy(StaggeredGridLayoutManager.GAP_HANDLING_NONE);

Do this ,spacing between the grid items stay same
private boolean flagDecoration = false;
if(!flagDecoration)
{
ItemOffsetDecoration itemDecoration = new ItemOffsetDecoration(getContext(),(R.dimen.spacing));
recyclerView.addItemDecoration(itemDecoration);
flagDecoration = true;
}

Related

Problem with Recyclerview not always loading items

Greetings to all the experts.
I have the following problem, I have implemented Billing Subscriptions in Android, the code works well, the problem is that the Recyclerview sometimes loads and many others it doesn't, sometimes it loads first, sometimes you have to enter the atricity a few times, I don't understand why it makes that bug, if you could check the code and guide me I would appreciate it
suscription.java
public class suscripcion extends AppCompatActivity implements PurchasesUpdatedListener {
private TextView statuspremium, rangopremium;
private BillingClient billingClient;
private AcknowledgePurchaseResponseListener acknowledgePurchaseResponseListener;
private RecyclerView recyclerView_sub;
private DocumentReference documentReference;
private FirebaseFirestore mFirestore;
private FirebaseUser user;
private FirebaseAuth fAuth;
private String userID;
private MySubAdapter adapter;
private Button cargarpremium;
private ImageView ayudapremium, txtclose;
private String status;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_suscripcion);
this.setFinishOnTouchOutside(false);
mFirestore = FirebaseFirestore.getInstance();
fAuth = FirebaseAuth.getInstance();
user = fAuth.getCurrentUser();
ayudapremium = findViewById(R.id.ayudapremium);
ayudapremium.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
startActivity(new Intent(suscripcion.this, ofrecepremium.class));
}
});
if (user !=null)
{
userID = fAuth.getCurrentUser().getUid();
}
documentReference = mFirestore.collection("users").document(userID);
documentReference.addSnapshotListener(new EventListener<DocumentSnapshot>() {
#Override
public void onEvent(#Nullable DocumentSnapshot value, #Nullable FirebaseFirestoreException error) {
status = value.getString("status");
}
});
setupSubClient();
iniciador();
txtclose = findViewById(R.id.txtclosesub);
txtclose.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
documentReference.update("premiumverificar", "no");
finish();
startActivity(new Intent(suscripcion.this, MainActivity.class));
}
});
}
private void iniciador() {
statuspremium = findViewById(R.id.statuspremium);
rangopremium = findViewById(R.id.rangopremium);
recyclerView_sub = findViewById(R.id.recyclerview_sub);
recyclerView_sub.setHasFixedSize(true);
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
recyclerView_sub.setLayoutManager(layoutManager);
recyclerView_sub.addItemDecoration(new DividerItemDecoration(this, layoutManager.getOrientation()));
cargarpremium = findViewById(R.id.cargarpremium);
cargarpremium.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
finish();
startActivity(getIntent());
}
});
}
private void loadAllSubPackage() {
if (billingClient.isReady())
{
Log.d("ERROR20", "onSkuDetailsResponse: ");
SkuDetailsParams params = SkuDetailsParams
.newBuilder()
.setSkusList(Arrays.asList("vf_sub_unmes", "vf_sub_tresmes"))
.setType(BillingClient.SkuType.SUBS)
.build();
billingClient.querySkuDetailsAsync(params, new SkuDetailsResponseListener() {
#Override
public void onSkuDetailsResponse(#NonNull BillingResult billingResult, #Nullable List<SkuDetails> list) {
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK)
{
adapter = new MySubAdapter(suscripcion.this, list, billingClient);
recyclerView_sub.setAdapter(adapter);
recyclerView_sub.setVisibility(View.VISIBLE);
Log.d("ERROR5", "onSkuDetailsResponse: " + billingResult.getResponseCode());
}else
{
Log.d("ERROR6", "onSkuDetailsResponse: " + billingResult.getResponseCode());
}
}
});
}else
{
loadAllSubPackage();
Log.d("ERROR7", "loadAllSubPackage: Billing cliente not ready" );
}
}
private void setupSubClient() {
acknowledgePurchaseResponseListener = new AcknowledgePurchaseResponseListener() {
#Override
public void onAcknowledgePurchaseResponse(#NonNull BillingResult billingResult) {
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK)
{
Log.d("ERROR1", "onAcknowledgePurchaseResponse: " +
billingResult.getResponseCode());
// rangopremium.setVisibility(View.VISIBLE);
// tiemposub.setVisibility(View.VISIBLE);
}else
{
Log.d("ERROR21", "onAcknowledgePurchaseResponse: " +
billingResult.getResponseCode());
}
}
};
billingClient = BillingClientSetup.getInstance(this, this);
billingClient.startConnection(new BillingClientStateListener() {
#Override
public void onBillingServiceDisconnected() {
setupSubClient();
Log.d("ERROR2", "onBillingServiceDisconnected: " );
}
#Override
public void onBillingSetupFinished(#NonNull BillingResult billingResult) {
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK)
{
Log.d("ERROR3", "onBillingSetupFinished: " +
billingResult.getResponseCode());
// //Query
// List<Purchase> purchases = billingClient.queryPurchases(BillingClient.SkuType.SUBS)
// .getPurchasesList();
billingClient.queryPurchasesAsync(BillingClient.SkuType.SUBS, new PurchasesResponseListener() {
#Override
public void onQueryPurchasesResponse(#NonNull BillingResult billingResult, #NonNull List<Purchase> list) {
if (list.size() > 0)
{
for (Purchase purchase:list)
handleSubAlreadyPurchase(purchase);
Log.d("ERROR16", "onBillingSetupFinished: " +
billingResult.getResponseCode());
}else
{
loadAllSubPackage();
documentReference.update("status", "user");
statuspremium.setVisibility(View.VISIBLE);
Log.d("ERROR17", "onBillingSetupFinished: " +
billingResult.getResponseCode());
// rangopremium.setVisibility(View.GONE);
// tiemposub.setVisibility(View.GONE);
// statuspremium.setVisibility(View.VISIBLE);
// recyclerView_sub.setVisibility(View.VISIBLE);
}
}
});
}
else
{
Log.d("ERROR4", "onBillingSetupFinished: " +
billingResult.getResponseCode());
}
}
});
}
private void handleSubAlreadyPurchase(Purchase purchases) {
if (purchases.getPurchaseState() == Purchase.PurchaseState.PURCHASED)
{
documentReference = mFirestore.collection("users").document(userID);
documentReference.addSnapshotListener(new EventListener<DocumentSnapshot>() {
#Override
public void onEvent(#Nullable DocumentSnapshot value, #Nullable FirebaseFirestoreException error) {
if (value != null && value.exists()) {
status = value.getString("status");
rangopremium.setVisibility(View.VISIBLE);
rangopremium.setText(status);
}
}
});
Log.d("ERROR11", "handleSubAlreadyPurchase: ");
if (!purchases.isAcknowledged())
{
Log.d("ERROR12", "handleSubAlreadyPurchase: ");
AcknowledgePurchaseParams acknowledgePurchaseParams = AcknowledgePurchaseParams.newBuilder()
.setPurchaseToken(purchases.getPurchaseToken())
.build();
billingClient.acknowledgePurchase(acknowledgePurchaseParams, acknowledgePurchaseResponseListener);
}else
{
Log.d("ERROR13", "handleSubAlreadyPurchase: ");
}
}
if (purchases.getPurchaseState() == Purchase.PurchaseState.UNSPECIFIED_STATE){
Log.d("ERROR14", "handleSubAlreadyPurchase: ");
}
}
#Override
public void onPurchasesUpdated(#NonNull BillingResult billingResult, #Nullable List<Purchase> list) {
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK && list !=null)
{
SimpleDateFormat formatter = new SimpleDateFormat("dd.MM.yyyy, HH:mm:ss", Locale.getDefault());
String currentDateandTime = formatter.format(new Date());
documentReference = mFirestore.collection("users").document(userID);
documentReference.addSnapshotListener(new EventListener<DocumentSnapshot>() {
#Override
public void onEvent(#Nullable DocumentSnapshot value, #Nullable FirebaseFirestoreException error) {
if (value != null && value.exists()){
status = value.getString("status");
rangopremium.setVisibility(View.VISIBLE);
rangopremium.setText(status);
}
}
});
statuspremium.setVisibility(View.GONE);
recyclerView_sub.setVisibility(View.GONE);
documentReference.update("status", "PREMIUM");
documentReference.update("bloqueo", 3);
documentReference.update("premiumfecha", currentDateandTime);
Log.d("ERROR8", "onPurchasesUpdated: " + billingResult.getResponseCode());
for (Purchase purchase:list)
handleSubAlreadyPurchase(purchase);
}else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.USER_CANCELED)
{
Log.d("ERROR9", "onPurchasesUpdated: " + billingResult.getResponseCode());
rangopremium.setVisibility(View.GONE);
statuspremium.setVisibility(View.VISIBLE);
recyclerView_sub.setVisibility(View.VISIBLE);
documentReference.update("status", "user");
}else
{
Log.d("ERROR10", "onPurchasesUpdated: " + billingResult.getResponseCode());
}
}
#Override
public void onBackPressed() {
}
}
MySubAdapter.java
public class MySubAdapter extends RecyclerView.Adapter<MySubAdapter.MyViewHolder> {
AppCompatActivity appCompatActivity;
List<SkuDetails> skuDetailsList;
BillingClient billingClient;
public MySubAdapter(AppCompatActivity appCompatActivity, List<SkuDetails> skuDetailsList, BillingClient billingClient) {
this.appCompatActivity = appCompatActivity;
this.skuDetailsList = skuDetailsList;
this.billingClient = billingClient;
}
#NonNull
#Override
public MyViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
return new MyViewHolder(LayoutInflater.from(appCompatActivity.getBaseContext())
.inflate(R.layout.listado_sub, parent, false));
}
#Override
public void onBindViewHolder(#NonNull MyViewHolder holder, int position) {
holder.txt_sub_name.setText(skuDetailsList.get(position).getTitle());
holder.txt_precio_sub.setText(skuDetailsList.get(position).getPrice());
holder.setListener(new IReciclerClickListener() {
#Override
public void onClick(View view, int position) {
BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder()
.setSkuDetails(skuDetailsList.get(position))
.build();
int reponse = billingClient.launchBillingFlow(appCompatActivity, billingFlowParams)
.getResponseCode();
switch(reponse)
{
case BillingClient.BillingResponseCode.BILLING_UNAVAILABLE:
Log.d("ERROR11", "BILLING_UNAVAILABLE");
break;
case BillingClient.BillingResponseCode.DEVELOPER_ERROR:
Log.d("ERROR12", "DEVELOPER_ERROR");
break;
case BillingClient.BillingResponseCode.FEATURE_NOT_SUPPORTED:
Log.d("ERROR13", "FEATURE_NOT_SUPPORTED");
break;
case BillingClient.BillingResponseCode.ITEM_ALREADY_OWNED:
Log.d("ERROR14", "ITEM_ALREADY_OWNED");
break;
case BillingClient.BillingResponseCode.SERVICE_DISCONNECTED:
Log.d("ERROR15", "SERVICE_DISCONNECTED");
break;
case BillingClient.BillingResponseCode.SERVICE_TIMEOUT:
Log.d("ERROR16", "SERVICE_TIMEOUT");
break;
case BillingClient.BillingResponseCode.ITEM_UNAVAILABLE:
Log.d("ERROR17", "ITEM_UNAVAILABLE");
break;
default:
break;
}
}
});
}
#Override
public int getItemCount() {
return skuDetailsList.size();
}
public static class MyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
TextView txt_sub_name, txt_precio_sub;
IReciclerClickListener listener;
public void setListener(IReciclerClickListener listener) {
this.listener = listener;
}
public MyViewHolder(#NonNull View itemView) {
super(itemView);
txt_sub_name = itemView.findViewById(R.id.txt_sub_name);
txt_precio_sub = itemView.findViewById(R.id.txt_precio_sub);
itemView.setOnClickListener(this);
}
#Override
public void onClick(View v) {
listener.onClick(v, getBindingAdapterPosition());
}
}
}
activity_suscription
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/Snackbar_apoyo"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/fondocurva"
android:orientation="vertical"
android:layout_gravity="center"
android:gravity="center">
<ImageView
android:contentDescription="#string/app_name"
android:id="#+id/txtclosesub"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/ic_close_black"
android:layout_gravity="start"
android:layout_marginStart="20dp"
android:layout_marginTop="20dp"
android:layout_marginBottom="20dp"
android:gravity="center"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center">
<androidx.appcompat.widget.AppCompatTextView
android:id="#+id/statuspremium"
style="#style/TextAppearance.AppCompat.Widget.ActionBar.Title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:gravity="center"
android:shadowColor="#color/negro"
android:shadowDx="2"
android:shadowDy="5"
android:visibility="gone"
android:shadowRadius="10"
android:text="#string/no_premium"
android:textColor="#color/colorAccent"
android:textSize="25sp"
android:textStyle="bold|italic" />
<com.hanks.htextview.rainbow.RainbowTextView
android:id="#+id/rangopremium"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:visibility="gone"
android:textSize="25sp"
android:textStyle="bold"
app:colorSpace="150dp"
app:colorSpeed="10dp"/>
<ImageView
android:contentDescription="#string/app_name"
android:layout_width="150dp"
android:layout_height="150dp"
android:src="#drawable/vinoayuda"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="#string/haste_premium"
android:textSize="18sp"
android:textColor="#color/colorAccent"
android:layout_gravity="center"
android:gravity="center"
android:paddingLeft="20dp"
android:paddingRight="20dp"
android:layout_marginBottom="20dp"
android:textStyle="bold"
android:fontFamily="sans-serif-smallcaps"
android:shadowColor="#color/negro"
android:shadowRadius="10"
android:shadowDx="2"
android:shadowDy="5"/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="30dp"
android:gravity="center"
android:layout_gravity="center"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="#string/que_ofrece_el_premium"
android:textSize="16sp"
android:textColor="#color/blanco"
android:layout_gravity="center"
android:gravity="center"
android:paddingLeft="20dp"
android:paddingRight="20dp"
android:textStyle="bold"
android:fontFamily="sans-serif-smallcaps" />
<ImageView
android:id="#+id/ayudapremium"
android:background="#drawable/curvas"
android:contentDescription="#string/app_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginEnd="10dp"
android:src="#drawable/ic_baseline_help"/>
</LinearLayout>
<Button
android:id="#+id/cargarpremium"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="#string/cargar_botones"
android:padding="10dp"
android:textColor="#color/colorPrimary"
android:background="#drawable/btn_desing"/>
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recyclerview_sub"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:layout_marginBottom="20dp">
</androidx.recyclerview.widget.RecyclerView>
</LinearLayout>
</LinearLayout>
PD: I have placed a button to reload the Activity, and only then sometimes it loads and sometimes it doesn't, just to half solve that bug, but the ideal is that the recyclerview loads me the first time

"Xamarin Android" How can i set the TextCell property of a ListView?

In Xamarin Forms we can set the TextCell property like this right?
<ListView x:Name="WorkersListView" BackgroundColor="#ECEFF1" Margin="2" ItemSelected="WorkersListView_ItemSelected">
<ListView.ItemTemplate>
<DataTemplate>
<TextCell TextColor="Black" Text="{Binding WorkerName}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
By writing ListView.ItemTemplate and DataTemplate we can access the TextCell
But how can i do it on Xamarin Android?
because it doesn't have ListView.ItemTemplate and DataTemplate elements.
This is my current ListView in my Xamarin.Android project
<ListView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/WorkersListView"
android:background="#android:color/darker_gray"
android:layout_margin="2dp">
</ListView>
Thanks in advance.
Working with ListView in Xamarin Forms and Xamarin Android is different
Add an XML that looks something like this:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="#+id/listView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fastScrollEnabled="true" />
</LinearLayout>
Then create ListView template
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:minWidth="25px"
android:minHeight="25px"
android:paddingBottom="4dp">
<ImageView
android:id="#+id/photoImageView"
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:padding="5dp" />
<TextView
android:id="#+id/nameTextView"
android:layout_toRightOf="#id/photoImageView"
android:text="Name"
android:textAppearance="?android:attr/textAppearanceLarge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="5dp" />
<TextView
android:id="#+id/departmentTextView"
android:layout_toRightOf="#id/photoImageView"
android:layout_below="#id/nameTextView"
android:text="Department"
android:textAppearance="?android:attr/textAppearanceSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="5dp" />
</RelativeLayout>
Create an adapter class something like:
public class MyCustomListAdapter : BaseAdapter<User>
{
List<User> users;
public MyCustomListAdapter(List<User> users)
{
this.users = users;
}
public override User this[int position]
{
get
{
return users[position];
}
}
public override int Count
{
get
{
return users.Count;
}
}
public override long GetItemId(int position)
{
return position;
}
public override View GetView(int position, View convertView, ViewGroup parent)
{
var view = convertView;
if(view==null)
{
view = LayoutInflater.From(parent.Context).Inflate(Resource.Layout.userRow, parent, false);
var photo = view.FindViewById<ImageView>(Resource.Id.photoImageView);
var name = view.FindViewById<TextView>(Resource.Id.nameTextView);
var department = view.FindViewById<TextView>(Resource.Id.departmentTextView);
view.Tag = new ViewHolder() { Photo = photo, Name = name, Department = department };
}
var holder = (ViewHolder)view.Tag;
holder.Photo.SetImageDrawable(ImageManager.Get(parent.Context, users[position].ImageUrl));
holder.Name.Text = users[position].Name;
holder.Department.Text = users[position].Department;
return view;
}
}
A detailed answer is in the blog:
https://www.c-sharpcorner.com/article/creating-custom-listview-in-xamarin-android-app/
ListView in Android instance requires an Adapter to consume the data contained in row Views.
Add one ListView and one TextView control, to display the list of items and the elements respectively. Use the following code.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#6BC6ED"
>
<TextView
android:id="#+id/Name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="25dp"
android:textColor="#FFFFFF"
/>
<ListView
android:id="#+id/demolist"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</ListView>
</LinearLayout>
Similar to what we did in Forms , create new model class which contains the binding property in ViewCell.
For example
public class Students {
public string Name {
get;
set;
}
public int Age {
get;
set;
}
}
Next, you need to create Adapter to bind the data to the ListView. Android has in-built base adapter class for Adapter implementations to bind a ListView to a data source. Also, we can get item position, count, and selected item, etc. there. Count method is used to get the number of rows in the list.
GetView method is used to return a View for each row, populated with which element.
GetItemId method is used to return row data properties like row number and value.
public class StudentAdapter: BaseAdapter < Students > {
public List < Students > sList;
private Context sContext;
public StudentAdapter(Context context, List < Students > list) {
sList = list;
sContext = context;
}
public override Students this[int position] {
get {
return sList[position];
}
}
public override int Count {
get {
return sList.Count;
}
}
public override long GetItemId(int position) {
return position;
}
public override View GetView(int position, View convertView, ViewGroup parent) {
View row = convertView;
try {
if (row == null) {
row = LayoutInflater.From(sContext).Inflate(Resource.Layout.Main, null, false);
}
TextView txtName = row.FindViewById < TextView > (Resource.Id.Name);
txtName.Text = sList[position].Name;
} catch (Exception ex) {
System.Diagnostics.Debug.WriteLine(ex.Message);
} finally {}
return row;
}
}
Next, open your activity file and write the below code to create a list of items to display a ListView and show the selected items from the list.
private ListView studentlistView;
private List < Students > mlist;
StudentAdapter adapter;
protected override void OnCreate(Bundle bundle) {
base.OnCreate(bundle);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.Main);
List < Students > objstud = new List < Students > ();
objstud.Add(new Students {
Name = "Jack", Age = 26
});
objstud.Add(new Students {
Name = "Mary", Age = 26
});
studentlistView = FindViewById < ListView > (Resource.Id.demolist);
mlist = new List < Students > ();
mlist = objstud;
adapter = new StudentAdapter(this, mlist);
studentlistView.Adapter = adapter;
studentlistView.ItemClick += StudentlistView_ItemClick;
}
To handle the selected items, write the below code.
private void StudentlistView_ItemClick(object sender, AdapterView.ItemClickEventArgs e) {
var select = mlist[e.Position].Name;
Android.Widget.Toast.MakeText(this, select, Android.Widget.ToastLength.Long).Show();
}
For more details you can check here .

How to assign radioButton to ListView in android

In my application, I have a ListView to contain the 5 quiz questions and their answers from a json file in server. I want the choices to be Radio Buttons but when my application runs on the emulator, I can see the questions and answers but after clicking one of the radio buttons, some other questions' answers Radio Buttons are checked. How can I prevent that?
Here is my adapter class:
public class FormSoruAdapter extends ArrayAdapter<FormSoru>{
List<FormSoru> formSoruList;
Context context;
private LayoutInflater mInflater;
//Constructor method
public FormSoruAdapter(Context context, List<FormSoru> objects){
super(context, 0, objects);
this.context = context;
this.mInflater = LayoutInflater.from(context);
formSoruList = objects;
}
#Override
public FormSoru getItem(int position){
return formSoruList.get(position);
}
#Override
public View getView(int position, View convertView, ViewGroup parent){
final ViewHolder vh;
if (convertView == null){
View view = mInflater.inflate(R.layout.list_soru,parent,false);
vh = ViewHolder.create((LinearLayout) view);
view.setTag(vh);
} else {
vh = (ViewHolder) convertView.getTag();
}
FormSoru item = getItem(position);
vh.textViewSoruNo.setText(item.getId());
vh.textViewSoru.setText(item.getSoru());
String cevap1 = item.getCevap1();
String cevap2 = item.getCevap2();
String cevap3 = item.getCevap3();
String cevap4 = item.getCevap4();
String cevap5 = item.getCevap5();
if (cevap1 == "" && cevap2 == "" && cevap3 == "" && cevap4 == "" && cevap5 == ""){
vh.radioButtonCevap1.setVisibility(View.GONE);
vh.radioButtonCevap2.setVisibility(View.GONE);
vh.radioButtonCevap3.setVisibility(View.GONE);
vh.radioButtonCevap4.setVisibility(View.GONE);
vh.radioButtonCevap5.setVisibility(View.GONE);
} else {
vh.radioButtonCevap1.setText(item.getCevap1());
vh.radioButtonCevap2.setText(item.getCevap2());
vh.radioButtonCevap3.setText(item.getCevap3());
vh.radioButtonCevap4.setText(item.getCevap4());
vh.radioButtonCevap5.setText(item.getCevap5());
}
return vh.rootView;
}
private static class ViewHolder {
public final LinearLayout rootView;
public final TextView textViewSoruNo;
public final TextView textViewSoru;
public final RadioButton radioButtonCevap1;
public final RadioButton radioButtonCevap2;
public final RadioButton radioButtonCevap3;
public final RadioButton radioButtonCevap4;
public final RadioButton radioButtonCevap5;
private ViewHolder(LinearLayout rootView, TextView textViewSoruNo, TextView textViewSoru, RadioButton radioButtonCevap1, RadioButton radioButtonCevap2, RadioButton radioButtonCevap3, RadioButton radioButtonCevap4, RadioButton radioButtonCevap5){
this.rootView = rootView;
this.textViewSoruNo = textViewSoruNo;
this.textViewSoru = textViewSoru;
this.radioButtonCevap1 = radioButtonCevap1;
this.radioButtonCevap2 = radioButtonCevap2;
this.radioButtonCevap3 = radioButtonCevap3;
this.radioButtonCevap4 = radioButtonCevap4;
this.radioButtonCevap5 = radioButtonCevap5;
}
public static ViewHolder create(LinearLayout rootView){
TextView textViewSoruNo = (TextView) rootView.findViewById(R.id.textViewSoruNo);
TextView textViewSoru = (TextView) rootView.findViewById(R.id.textViewSoru);
RadioButton radioButtonCevap1 = (RadioButton) rootView.findViewById(R.id.radioCevap1);
RadioButton radioButtonCevap2 = (RadioButton) rootView.findViewById(R.id.radioCevap2);
RadioButton radioButtonCevap3 = (RadioButton) rootView.findViewById(R.id.radioCevap3);
RadioButton radioButtonCevap4 = (RadioButton) rootView.findViewById(R.id.radioCevap4);
RadioButton radioButtonCevap5 = (RadioButton) rootView.findViewById(R.id.radioCevap5);
return new ViewHolder(rootView, textViewSoruNo, textViewSoru, radioButtonCevap1, radioButtonCevap2,
radioButtonCevap3, radioButtonCevap4,radioButtonCevap5);
}
}
}
And my fragment is:
public class FormFragment extends Fragment {
private ListView listView;
private View parentView;
private ArrayList<FormSoru> formSoruList;
private FormSoruAdapter adapter;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_anket_soru, container, false);
}
#Override
public void onViewCreated(final View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
formSoruList = new ArrayList<>();
parentView = view.findViewById(R.id.parentLayout);
listView = (ListView) view.findViewById(R.id.listViewSorular);
APIService apıService = RetroClient.getApiService();
Call<FormSorular> call = apıService.getFormSorular();
call.enqueue(new Callback<FormSorular>() {
#Override
public void onResponse(Call<FormSorular> call, Response<FormSorular> response) {
if (response.isSuccessful()){
formSoruList = response.body().getFormSorular();
adapter = new FormSoruAdapter(getActivity(),formSoruList);
listView.setAdapter(adapter);
}
}
#Override
public void onFailure(Call<FormSorular> call, Throwable t) {
}
});
}
}
My fragment's layout is:
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/parentLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Dear User"
android:layout_margin="10dp"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Welcome"
android:layout_margin="8dp"/>
<ListView
android:id="#+id/listViewSorular"
android:layout_width="match_parent"
android:layout_height="match_parent">
</ListView>
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Görüşleriniz"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Gönder"/>
</LinearLayout>
And my list_soru layout which contains questions is:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/textViewSoruNo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Soru No"
android:layout_margin="10dp"/>
<TextView
android:id="#+id/textViewSoru"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Soru"
android:layout_margin="8dp"/>
<RadioGroup
android:id="#+id/radioGroup"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<RadioButton
android:id="#+id/radioCevap1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="cevap1"/>
<RadioButton
android:id="#+id/radioCevap2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="cevap2"/>
<RadioButton
android:id="#+id/radioCevap3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="cevap3"/>
<RadioButton
android:id="#+id/radioCevap4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="cevap4"/>
<RadioButton
android:id="#+id/radioCevap5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="cevap5"/>
</RadioGroup>

How to draw or write text by hand on image selected from gallery in android?

Actually I have written a code to select image from gallery,but I dont know how to draw or write a text by hand on it.and Edited image should be saved separately in sdcard.Basically I have to make basic paint app where canvas will be my selected image,and my touch is input to draw a circle or anything.
package listdisplay.example.com.photoedit;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.Display;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import java.io.FileDescriptor;
import java.io.IOException;
public class MainActivity_photoedit extends AppCompatActivity {
private static final int RORC =0;
ImageView iview;
Button button,button2;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_photoedit);
iview= (ImageView) findViewById(R.id.imageView);
button =(Button)findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("image/*");
startActivityForResult(intent,RORC);
}
}
);
}
#Override
public void onActivityResult(int requestCode,int resultCode,Intent resultData){
if(requestCode==RORC && resultCode==RESULT_OK){
Uri uri =null;
if(resultData!=null){
uri=resultData.getData();
try {
Bitmap bitmap= getBitmapFromUri(uri);
iview.setImageBitmap(bitmap);
}
catch (IOException e){
e.printStackTrace();
}
}
}
}
private Bitmap getBitmapFromUri(Uri uri) throws IOException{
ParcelFileDescriptor parcelFileDescriptor = getContentResolver().openFileDescriptor(uri,"r");
FileDescriptor fileDescriptor= parcelFileDescriptor.getFileDescriptor();
Bitmap bitmap = BitmapFactory.decodeFileDescriptor(fileDescriptor);
parcelFileDescriptor.close();
return bitmap;
}
}
Also, there is another way to do like this is using FingerPaint Api.
For that download code from here,
"https://github.com/nikt/Finger-Paint"
Yes,It also not using Image-view, But it will pick image from gallery and set it into Draw-view and paint on that image.You can also try this.
I have find solution for your requirement . So try this,But for that you have to take Drawing-view instead of Image-view to draw or write text on Image which is picked from Gallery.
Hereby, I am posting code.
activity_main.xml :
<LinearLayout 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:gravity="center"
android:orientation="vertical"
android:paddingBottom="5dp"
android:paddingTop="5dp"
tools:context=".MainActivity">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageButton
android:id="#+id/buttonNew"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:contentDescription="#string/start_new"
android:src="#drawable/new_pic" />
<ImageButton
android:id="#+id/buttonBrush"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:contentDescription="#string/brush"
android:src="#drawable/brush" />
<ImageButton
android:id="#+id/buttonErase"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:contentDescription="#string/erase"
android:src="#drawable/eraser" />
<ImageButton
android:id="#+id/buttonSave"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:contentDescription="#string/save"
android:src="#drawable/save" />
<ImageButton
android:id="#+id/buttonPickImage"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:contentDescription="#string/save"
android:src="#drawable/save" />
</LinearLayout>
<com.wordpress.priyankvex.paintapp.DrawingView
android:id="#+id/drawing"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginBottom="3dp"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:layout_marginTop="3dp"
android:layout_weight="1"
android:background="#color/white" />
<!-- Color pallet -->
<LinearLayout
android:id="#+id/paint_colors"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageButton
android:layout_width="#dimen/large_brush"
android:layout_height="#dimen/large_brush"
android:layout_margin="2dp"
android:background="#color/skin"
android:contentDescription="#string/paint"
android:onClick="paintClicked"
android:src="#drawable/pallet"
android:tag="#color/skin" />
<ImageButton
android:layout_width="#dimen/large_brush"
android:layout_height="#dimen/large_brush"
android:layout_margin="2dp"
android:background="#color/black"
android:contentDescription="#string/paint"
android:onClick="paintClicked"
android:src="#drawable/pallet"
android:tag="#color/black" />
<ImageButton
android:layout_width="#dimen/large_brush"
android:layout_height="#dimen/large_brush"
android:layout_margin="2dp"
android:background="#color/red"
android:contentDescription="#string/paint"
android:onClick="paintClicked"
android:src="#drawable/pallet"
android:tag="#color/red" />
<ImageButton
android:layout_width="#dimen/large_brush"
android:layout_height="#dimen/large_brush"
android:layout_margin="2dp"
android:background="#color/green"
android:contentDescription="#string/paint"
android:onClick="paintClicked"
android:src="#drawable/pallet"
android:tag="#color/green" />
<ImageButton
android:layout_width="#dimen/large_brush"
android:layout_height="#dimen/large_brush"
android:layout_margin="2dp"
android:background="#color/blue"
android:contentDescription="#string/paint"
android:onClick="paintClicked"
android:src="#drawable/pallet"
android:tag="#color/blue" />
<ImageButton
android:layout_width="#dimen/large_brush"
android:layout_height="#dimen/large_brush"
android:layout_margin="2dp"
android:background="#color/yellow"
android:contentDescription="#string/paint"
android:onClick="paintClicked"
android:src="#drawable/pallet"
android:tag="#color/yellow" />
</LinearLayout>
</LinearLayout>
MainActivity.java :
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.MediaStore;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.Toast;
import java.util.UUID;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private DrawingView mDrawingView;
private ImageButton currPaint, drawButton, eraseButton, newButton, saveButton, buttonPickImage;
private float smallBrush, mediumBrush, largeBrush;
private final int RESULT_LOAD_IMAGE = 20;
private Uri pickedImage;
private String imagePath = "";
public Bitmap PaintBitmap;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mDrawingView = (DrawingView) findViewById(R.id.drawing);
// Getting the initial paint color.
LinearLayout paintLayout = (LinearLayout) findViewById(R.id.paint_colors);
// 0th child is white color, so selecting first child to give black as initial color.
currPaint = (ImageButton) paintLayout.getChildAt(1);
currPaint.setImageDrawable(getResources().getDrawable(R.drawable.pallet_pressed));
drawButton = (ImageButton) findViewById(R.id.buttonBrush);
drawButton.setOnClickListener(this);
eraseButton = (ImageButton) findViewById(R.id.buttonErase);
eraseButton.setOnClickListener(this);
newButton = (ImageButton) findViewById(R.id.buttonNew);
newButton.setOnClickListener(this);
saveButton = (ImageButton) findViewById(R.id.buttonSave);
saveButton.setOnClickListener(this);
buttonPickImage = (ImageButton) findViewById(R.id.buttonPickImage);
buttonPickImage.setOnClickListener(this);
smallBrush = getResources().getInteger(R.integer.small_size);
mediumBrush = getResources().getInteger(R.integer.medium_size);
largeBrush = getResources().getInteger(R.integer.large_size);
// Set the initial brush size
mDrawingView.setBrushSize(mediumBrush);
}
/**
* Method is called when color is clicked from pallet.
*
* #param view ImageButton on which click took place.
*/
public void paintClicked(View view) {
if (view != currPaint) {
// Update the color
ImageButton imageButton = (ImageButton) view;
String colorTag = imageButton.getTag().toString();
mDrawingView.setColor(colorTag);
// Swap the backgrounds for last active and currently active image button.
imageButton.setImageDrawable(getResources().getDrawable(R.drawable.pallet_pressed));
currPaint.setImageDrawable(getResources().getDrawable(R.drawable.pallet));
currPaint = (ImageButton) view;
mDrawingView.setErase(false);
mDrawingView.setBrushSize(mDrawingView.getLastBrushSize());
}
}
#Override
public void onClick(View v) {
int id = v.getId();
switch (id) {
case R.id.buttonBrush:
// Show brush size chooser dialog
showBrushSizeChooserDialog();
break;
case R.id.buttonErase:
// Show eraser size chooser dialog
showEraserSizeChooserDialog();
break;
case R.id.buttonNew:
// Show new painting alert dialog
showNewPaintingAlertDialog();
break;
case R.id.buttonSave:
// Show save painting confirmation dialog.
showSavePaintingConfirmationDialog();
break;
case R.id.buttonPickImage:
// Show save painting confirmation dialog.
Intent i = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(i, RESULT_LOAD_IMAGE);
break;
}
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == RESULT_LOAD_IMAGE && resultCode == RESULT_OK) {
pickedImage = data.getData();
String[] filePath = {MediaStore.Images.Media.DATA};
Cursor cursor = getContentResolver().query(pickedImage, filePath, null, null, null);
cursor.moveToFirst();
imagePath = cursor.getString(cursor.getColumnIndex(filePath[0]));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
// set Gallery Image to Drawing-view
if (imagePath != null) {
PaintBitmap = getResizeBitmap(imagePath);
BitmapDrawable ob = new BitmapDrawable(getResources(), PaintBitmap);
mDrawingView.setBackgroundDrawable(ob);
}
}
}
}
public static Bitmap getResizeBitmap(String path) {
BitmapFactory.Options op = new BitmapFactory.Options();
op.inPreferredConfig = Bitmap.Config.ARGB_8888;
Bitmap bmap = BitmapFactory.decodeFile(path, op);
int nh = (int) (bmap.getHeight() * (1024.0 / bmap.getWidth()));
Bitmap bitmapThumbnail = Bitmap.createScaledBitmap(bmap, 1024, nh, true);
int bitmapWidthMain = bitmapThumbnail.getWidth();
int bitmapHeightMain = bitmapThumbnail.getHeight();
return bitmapThumbnail;
}
private void showBrushSizeChooserDialog() {
final Dialog brushDialog = new Dialog(this);
brushDialog.setContentView(R.layout.dialog_brush_size);
brushDialog.setTitle("Brush size:");
ImageButton smallBtn = (ImageButton) brushDialog.findViewById(R.id.small_brush);
smallBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mDrawingView.setBrushSize(smallBrush);
mDrawingView.setLastBrushSize(smallBrush);
brushDialog.dismiss();
}
});
ImageButton mediumBtn = (ImageButton) brushDialog.findViewById(R.id.medium_brush);
mediumBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mDrawingView.setBrushSize(mediumBrush);
mDrawingView.setLastBrushSize(mediumBrush);
brushDialog.dismiss();
}
});
ImageButton largeBtn = (ImageButton) brushDialog.findViewById(R.id.large_brush);
largeBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mDrawingView.setBrushSize(largeBrush);
mDrawingView.setLastBrushSize(largeBrush);
brushDialog.dismiss();
}
});
mDrawingView.setErase(false);
brushDialog.show();
}
private void showEraserSizeChooserDialog() {
final Dialog brushDialog = new Dialog(this);
brushDialog.setTitle("Eraser size:");
brushDialog.setContentView(R.layout.dialog_brush_size);
ImageButton smallBtn = (ImageButton) brushDialog.findViewById(R.id.small_brush);
smallBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mDrawingView.setErase(true);
mDrawingView.setBrushSize(smallBrush);
brushDialog.dismiss();
}
});
ImageButton mediumBtn = (ImageButton) brushDialog.findViewById(R.id.medium_brush);
mediumBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mDrawingView.setErase(true);
mDrawingView.setBrushSize(mediumBrush);
brushDialog.dismiss();
}
});
ImageButton largeBtn = (ImageButton) brushDialog.findViewById(R.id.large_brush);
largeBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mDrawingView.setErase(true);
mDrawingView.setBrushSize(largeBrush);
brushDialog.dismiss();
}
});
brushDialog.show();
}
private void showNewPaintingAlertDialog() {
AlertDialog.Builder newDialog = new AlertDialog.Builder(this);
newDialog.setTitle("New drawing");
newDialog.setMessage("Start new drawing (you will lose the current drawing)?");
newDialog.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
mDrawingView.startNew();
dialog.dismiss();
}
});
newDialog.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
newDialog.show();
}
private void showSavePaintingConfirmationDialog() {
AlertDialog.Builder saveDialog = new AlertDialog.Builder(this);
saveDialog.setTitle("Save drawing");
saveDialog.setMessage("Save drawing to device Gallery?");
saveDialog.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
//save drawing
mDrawingView.setDrawingCacheEnabled(true);
String imgSaved = MediaStore.Images.Media.insertImage(
getContentResolver(), mDrawingView.getDrawingCache(),
UUID.randomUUID().toString() + ".png", "drawing");
if (imgSaved != null) {
Toast savedToast = Toast.makeText(getApplicationContext(),
"Drawing saved to Gallery!", Toast.LENGTH_SHORT);
savedToast.show();
} else {
Toast unsavedToast = Toast.makeText(getApplicationContext(),
"Oops! Image could not be saved.", Toast.LENGTH_SHORT);
unsavedToast.show();
}
// Destroy the current cache.
mDrawingView.destroyDrawingCache();
}
});
saveDialog.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
saveDialog.show();
}
}
Manifest.xml :
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
Try This code, It include brush for draw text on image with different brush size,eraser and color picker to draw text in different colors on text and yes, It will save image with text in gallery.
Hereby, i am attaching screenshot,
Hope, it will help you. :)

RecyclerView and Glide messing up the layout

I am running into a strange issue with the RecyclerView loading images dynamically with Glide. Looks like race condition and I looked at this thread but it did not solve the problem:
Recyclerview Adapter and Glide - same image every 4-5 rows
Here's how it looks like screenshot and the red arrows shows the image glitch, notice that the user name text ALWAYS exist.
The code itself looks pretty straight forward:
RV Adapter:
#Override
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
int viewType = getItemViewType(position);
bindSubscribersCell((SubscribersListViewHolder) viewHolder, position);
}
binding view:
private void bindSubscribersCell(SubscribersListViewHolder holder, int position) {
SubscribersListRowTypeUser subscriberRow = (SubscribersListRowTypeUser) mItems.get(position);
String userFullName = SDKUserHelper.MakeFullName(subscriberRow.getUser());
holder.getUsername().setText(userFullName);
holder.getUsername().setVisibility(View.VISIBLE);
CircleImageView userImageView = holder.getAvatarImage();
Drawable defaultIcon = ContextCompat.getDrawable(
userImageView.getContext(),
R.drawable.avatar_guest);
RecylerViewHelper.loadImageView(
subscriberRow.getUser().getPhotoUrl(),
userImageView,
defaultIcon,
true);
}
the image load helper using the same technique described on the thread I mentioned above:
public static void loadImageView(
String imageUrl,
ImageView imageView,
Drawable drawable,
boolean icon) {
Context context = imageView.getContext();
if (imageUrl != null && !imageUrl.isEmpty()) {
if (icon) {
Glide.with(context).using(CloudinaryHelper.getUrlLoaderPresetPOIIcon(context)).
load(imageUrl).
centerCrop().
crossFade().
into(imageView);
} else {
Glide.with(context).
load(imageUrl).
crossFade().
into(imageView);
}
} else {
Glide.clear(imageView);
imageView.setImageDrawable(drawable);
}
}
the inner layout of the cell:
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical">
<de.hdodenhof.circleimageview.CircleImageView
android:id="#+id/user_avatar"
android:layout_width="wrap_content"
android:layout_height="#dimen/splash_icon_height"
android:layout_centerVertical="true"
android:src="#drawable/avatar_guest" />
<TextView
android:id="#+id/user_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginEnd="40dp"
android:layout_toEndOf="#+id/user_avatar"
android:ellipsize="end"
android:gravity="center"
android:visibility="gone"
android:maxLines="1"
android:text=""
android:textSize="14sp" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:layout_marginEnd="20dp"
android:src="#drawable/arrow_forward_gray" />
</RelativeLayout>
the wrapper class to fetch the layout controls:
public class SubscribersListViewHolder extends RecyclerView.ViewHolder {
final private CircleImageView mAvatarImage;
final private TextView mUsername;
final private View mViewHolder;
public SubscribersListViewHolder(View itemView) {
super(itemView);
mViewHolder = itemView;
mUsername = (TextView) itemView.findViewById(R.id.user_name);
assert mUsername != null;
mAvatarImage = (CircleImageView) itemView.findViewById(R.id.user_avatar);
assert mAvatarImage != null;
}
public TextView getUsername() { return mUsername; }
public CircleImageView getAvatarImage() { return mAvatarImage; }
public View getViewHolder() { return mViewHolder; }
}
If anyone can spot the race condition, I really appreciate.
thank you!
The fix was to set the icon width and height to a fixed value, so instead of "wrap_content" make some fixed dp (in my case 49dp). In other words:
WRONG (race condition)
android:layout_width="wrap_content"
android:layout_height="#dimen/splash_icon_height"
CORRECT:
android:layout_width="#dimen/splash_icon_height"
android:layout_height="#dimen/splash_icon_height"
But the question now becomes why? I have no idea.