Seguridad

Enlace de datos de Android en RecyclerView – Pantalla de perfil

En mi artículo anterior, aprendimos los conceptos básicos del enlace de datos. Hoy vamos a poner en práctica lo básico mediante la implementación de una pantalla de perfil con conceptos de enlace de datos. La pantalla de perfil tiene detalles de perfil en la parte superior y la siguiente sección tiene imágenes de publicaciones en formato de trama. La cuadrícula se crea utilizando un RecyclerView que implementa el enlace de datos en la clase del adaptador.

El uso de DataBinding en una clase de adaptador mantiene el código al mínimo, ya que muchas cosas se tienen en cuenta en el diseño en sí.

1. Requisito

Este ejemplo requiere un conocimiento básico del enlace de datos de Android. Comience con DataBinding leyendo el tutorial a continuación.

Leer: Android funciona con enlace de datos

2. Crea un nuevo proyecto

1. Crea un nuevo proyecto en Android Studio Archivo ⇒ nuevo proyecto y seleccione Actividad basica de plantillas.

2. Activar DataBiding en app / build.gradle. También agregue las dependencias RecyclerView y Glide y Sincronizar el proyecto.

android 
    dataBinding 
        enabled = true
    


dependencies 
    //...

    implementation 'com.github.bumptech.glide:glide:4.6.1'
    annotationProcessor 'com.github.bumptech.glide:compiler:4.6.1'

    implementation 'com.android.support:recyclerview-v7:27.1.0'

3. Agregar INTERNET Permiso en AndroidManifest.xml ya que las imágenes deben cargarse desde una URL.

<uses-permission android:name="android.permission.INTERNET" />

Cuarto. Descarga res.zip y agrégalo a tus proyectos res Carpeta. Estas carpetas de caracteres contienen el símbolo más requerido para FAB.

5. Agregue los siguientes recursos a cada uno string.xml, dimension.xml y Colors.xml

<resources>
    <string name="app_name">Data Binding</string>
    <string name="action_settings">Settings</string>
    <string name="toolbar_profile">Profile</string>
    <string name="posts">POSTS</string>
    <string name="followers">FOLLOWERS</string>
    <string name="following">FOLLOWING</string>
</resources>
<resources>
    <dimen name="fab_margin">16dp</dimen>
    <dimen name="activity_margin">16dp</dimen>
    <dimen name="dimen_8dp">8dp</dimen>
    <dimen name="profile_image">100dp</dimen>
    <dimen name="fab_profile">30dp</dimen>
    <dimen name="profile_name">15dp</dimen>
    <dimen name="profile_about">13dp</dimen>
    <dimen name="profile_meta">24dp</dimen>
    <dimen name="profile_meta_label">10dp</dimen>
</resources>
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="colorPrimary">#222222</color>
    <color name="colorPrimaryDark">#111111</color>
    <color name="colorAccent">#fecb2f</color>
    <color name="profile_meta">#333</color>
</resources>

Sexto. Crea tres paquetes con el nombre modelo, Utils y vista. Una vez creado, mueva el Actividad principal a vista Paquete.

A continuación se muestra la estructura final del proyecto y los archivos requeridos.

Séptimo. Crear usuario Clase bajo modelo Paquete. Extienda la clase de para hacer que esta clase sea observable BaseObservable.

Ambos para demostración Observable y Campo observable se utilizan en la misma clase.

  • Para nombres de variables, correo electrónico, profileImage y aproximadamente., @Bindable Se utiliza nota y notificarPropertyChanged se llama cuando se establecen nuevos datos
  • variables Numero de publicaciones, Numero de seguidores, numberOfFollowing se declaran como Campos observables
  • @BindingAdapter se usa para atar foto de perfil a Vista de imagen para cargar la imagen desde la URL usando la biblioteca Glide.
import android.databinding.BaseObservable;
import android.databinding.Bindable;
import android.databinding.BindingAdapter;
import android.databinding.ObservableField;
import android.widget.ImageView;

import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;

import info.androidhive.databinding.BR;

public class User extends BaseObservable 
    String name;
    String email;
    String profileImage;
    String about;


    // profile meta fields are ObservableField, will update the UI
    // whenever a new value is set
    public ObservableField<Long> numberOfFollowers = new ObservableField<>();
    public ObservableField<Long> numberOfPosts = new ObservableField<>();
    public ObservableField<Long> numberOfFollowing = new ObservableField<>();

    public User() 
    

    @Bindable
    public String getName() 
        return name;
    

    public void setName(String name) 
        this.name = name;
        notifyPropertyChanged(BR.name);
    

    @Bindable
    public String getEmail() 
        return email;
    

    public void setEmail(String email) 
        this.email = email;
        notifyPropertyChanged(BR.email);
    

    @BindingAdapter("profileImage")
    public static void loadImage(ImageView view, String imageUrl) 
        Glide.with(view.getContext())
                .load(imageUrl)
                .apply(RequestOptions.circleCropTransform())
                .into(view);

        // If you consider Picasso, follow the below
        // Picasso.with(view.getContext()).load(imageUrl).placeholder(R.drawable.placeholder).into(view);
    

    @Bindable
    public String getProfileImage() 
        return profileImage;
    

    public void setProfileImage(String profileImage) 
        this.profileImage = profileImage;
        notifyPropertyChanged(BR.profileImage);
    

    @Bindable
    public String getAbout() 
        return about;
    

    public void setAbout(String about) 
        this.about = about;
        notifyPropertyChanged(BR.about);
    

    public ObservableField<Long> getNumberOfFollowers() 
        return numberOfFollowers;
    

    public ObservableField<Long> getNumberOfPosts() 
        return numberOfPosts;
    

    public ObservableField<Long> getNumberOfFollowing() 
        return numberOfFollowing;
    

Octavo. Crea otra clase llamada Post.java debajo modelo Paquete. Esta clase de modelo proporciona datos RecyclerView.

import android.databinding.BindingAdapter;
import android.widget.ImageView;

import com.bumptech.glide.Glide;

public class Post 
    String imageUrl;

    @BindingAdapter("imageUrl")
    public static void loadImage(ImageView view, String imageUrl) 
        Glide.with(view.getContext())
                .load(imageUrl)
                .into(view);
    

    public String getImageUrl() 
        return imageUrl;
    

    public void setImageUrl(String imageUrl) 
        this.imageUrl = imageUrl;
    

9. Debajo Utils Paquete, crea dos clases con el nombre BindingUtils.java y GridSpacingItemDecoration.java

  • convertToSuffix () El método convierte un número en un formato legible por humanos. Por ejemplo, 5500L se convertirá como 5,5 km y 5050890L se convertirá como 5,1 metros.
  • Vinculamos esta función a TextViews para mostrar las publicaciones, los seguidores y los seguidores en un formato legible por humanos.
package info.androidhive.databinding.utils;

public class BindingUtils 

    // https://stackoverflow.com/questions/9769554/how-to-convert-number-into-k-thousands-m-million-and-b-billion-suffix-in-jsp
    // Converts the number to K, M suffix
    // Ex: 5500 will be displayed as 5.5k
    public static String convertToSuffix(long count) 
        if (count < 1000) return "" + count;
        int exp = (int) (Math.log(count) / Math.log(1000));
        return String.format("%.1f%c",
                count / Math.pow(1000, exp),
                "kmgtpe".charAt(exp - 1));
    

GridSpacingItemDecoration Proporciona espacio entre los elementos de la cuadrícula de RecyclerView.

package info.androidhive.databinding.utils;

import android.graphics.Rect;
import android.support.v7.widget.RecyclerView;
import android.view.View;

public class GridSpacingItemDecoration extends RecyclerView.ItemDecoration 

    private int spanCount;
    private int spacing;
    private boolean includeEdge;

    public GridSpacingItemDecoration(int spanCount, int spacing, boolean includeEdge) 
        this.spanCount = spanCount;
        this.spacing = spacing;
        this.includeEdge = includeEdge;
    

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) 
        int position = parent.getChildAdapterPosition(view);
        int column = position % spanCount;

        if (includeEdge) 
            outRect.left = spacing - column * spacing / spanCount;
            outRect.right = (column + 1) * spacing / spanCount;

            if (position < spanCount) 
                outRect.top = spacing;
            
            outRect.bottom = spacing;
         else 
            outRect.left = column * spacing / spanCount;
            outRect.right = spacing - (column + 1) * spacing / spanCount;
            if (position >= spanCount) 
                outRect.top = spacing;
            
        
    

2.1 Enlace de datos en RecyclerView

La vinculación de un diseño RecyclerView es similar a la vinculación normal, excepto que se han realizado pocos cambios en los métodos onCreateViewHolder y onBindViewHolder.

10. Crea un diseño llamado post_row_item.xml. Este diseño incluye un Vista de imagen para renderizar la imagen en RecyclerView.

  • En este diseño, el enlace de datos se habilita manteniendo el elemento raíz como . La correo Modelo vinculado a este diseño con Etiqueta.
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:bind="http://schemas.android.com/apk/res/android">

    <data>

        <variable
            name="post"
            type="info.androidhive.databinding.model.Post" />
    </data>

    <android.support.constraint.ConstraintLayout 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="wrap_content">

        <ImageView
            android:id="@+id/thumbnail"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:scaleType="centerCrop"
            bind:imageUrl="@post.imageUrl"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintDimensionRatio="H,1:1"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

    </android.support.constraint.ConstraintLayout>
</layout>

11. Crea una clase llamada PostsAdapter.java debajo vista Paquete.

  • Cuál es el nombre del diseño post_row_item.xmlserá la clase de enlace generada PostRowItemBinding.
  • en el onCreateViewHolder () Método, el diseño de post_row_item se infla con la ayuda de PostRowItemBinding Clase.
  • holder.binding.setPost () une el correo Modelo para cada fila.
package info.androidhive.databinding.view;

import android.databinding.DataBindingUtil;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import java.util.List;

import info.androidhive.databinding.R;
import info.androidhive.databinding.databinding.PostRowItemBinding;
import info.androidhive.databinding.model.Post;

public class PostsAdapter extends RecyclerView.Adapter<PostsAdapter.MyViewHolder> {

    private List<Post> postList;
    private LayoutInflater layoutInflater;
    private PostsAdapterListener listener;

    public class MyViewHolder extends RecyclerView.ViewHolder 

        private final PostRowItemBinding binding;

        public MyViewHolder(final PostRowItemBinding itemBinding) 
            super(itemBinding.getRoot());
            this.binding = itemBinding;
        
    


    public PostsAdapter(List<Post> postList, PostsAdapterListener listener) 
        this.postList = postList;
        this.listener = listener;
    

    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) 
        if (layoutInflater == null) 
            layoutInflater = LayoutInflater.from(parent.getContext());
        
        PostRowItemBinding binding =
                DataBindingUtil.inflate(layoutInflater, R.layout.post_row_item, parent, false);
        return new MyViewHolder(binding);
    

    @Override
    public void onBindViewHolder(MyViewHolder holder, final int position) 
        holder.binding.setPost(postList.get(position));
        holder.binding.thumbnail.setOnClickListener(new View.OnClickListener() 
            @Override
            public void onClick(View v) 
                if (listener != null) 
                    listener.onPostClicked(postList.get(position));
                
            
        );
    

    @Override
    public int getItemCount() 
        return postList.size();
    

    public interface PostsAdapterListener 
        void onPostClicked(Post post);
    
}

2.2 Creación de la pantalla de perfil

Ahora tenemos todos los archivos en su lugar. Comencemos con la construcción de la interfaz principal.

12º. Abra los archivos de diseño de actividad principal, es decir activity_main.xml y content_main.xml y activar el enlace de datos agregando , y Palabras clave.

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:bind="http://schemas.android.com/apk/res/android">

    <data>

        <variable
            name="user"
            type="info.androidhive.databinding.model.User" />
    </data>

    <android.support.design.widget.CoordinatorLayout xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".view.MainActivity">

        <android.support.design.widget.AppBarLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:elevation="0dp"
            android:theme="@style/AppTheme.AppBarOverlay">

            <android.support.v7.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                android:background="?attr/colorPrimary"
                app:popupTheme="@style/AppTheme.PopupOverlay" />

        </android.support.design.widget.AppBarLayout>

        <include
            android:id="@+id/content"
            layout="@layout/content_main"
            bind:user="@user" />

    </android.support.design.widget.CoordinatorLayout>
</layout>
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:bind="http://schemas.android.com/apk/res/android">

    <data>

        <import type="info.androidhive.databinding.utils.BindingUtils" />

        <variable
            name="user"
            type="info.androidhive.databinding.model.User" />

        <variable
            name="handlers"
            type="info.androidhive.databinding.view.MainActivity.MyClickHandlers" />
    </data>

    <android.support.v4.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:focusableInTouchMode="true"
            android:orientation="vertical"
            tools:context=".view.MainActivity"
            tools:showIn="@layout/activity_main">

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:background="@color/colorPrimary"
                android:orientation="vertical"
                android:paddingBottom="@dimen/activity_margin"
                android:paddingTop="@dimen/activity_margin">

                <RelativeLayout
                    android:layout_width="@dimen/profile_image"
                    android:layout_height="@dimen/profile_image"
                    android:layout_gravity="center_horizontal">

                    <ImageView
                        android:id="@+id/profile_image"
                        android:layout_width="@dimen/profile_image"
                        android:layout_height="@dimen/profile_image"
                        android:layout_centerHorizontal="true"
                        android:onLongClick="@handlers::onProfileImageLongPressed"
                        bind:profileImage="@user.profileImage" />

                    <android.support.design.widget.FloatingActionButton
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_alignParentBottom="true"
                        android:layout_alignParentRight="true"
                        android:onClick="@handlers::onProfileFabClicked"
                        android:src="@drawable/ic_add_white_24dp"
                        app:fabCustomSize="@dimen/fab_profile" />

                </RelativeLayout>


                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_gravity="center_horizontal"
                    android:layout_marginTop="@dimen/dimen_8dp"
                    android:fontFamily="sans-serif"
                    android:letterSpacing="0.1"
                    android:text="@user.name"
                    android:textColor="@android:color/white"
                    android:textSize="@dimen/profile_name"
                    android:textStyle="bold" />

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_gravity="center_horizontal"
                    android:fontFamily="sans-serif"
                    android:letterSpacing="0.1"
                    android:text="@user.about"
                    android:textColor="@android:color/white"
                    android:textSize="@dimen/profile_about" />

            </LinearLayout>

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginBottom="@dimen/activity_margin"
                android:layout_marginTop="@dimen/fab_margin"
                android:orientation="horizontal"
                android:weightSum="3">

                <LinearLayout
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:layout_weight="1"
                    android:gravity="center_horizontal"
                    android:onClick="@handlers::onPostsClicked"
                    android:orientation="vertical">

                    <TextView
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:fontFamily="sans-serif-condensed"
                        android:text="@BindingUtils.convertToSuffix(user.numberOfPosts)"
                        android:textColor="@color/profile_meta"
                        android:textSize="24dp"
                        android:textStyle="normal" />

                    <TextView
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="@string/posts"
                        android:textSize="@dimen/profile_meta_label" />

                </LinearLayout>

                <LinearLayout
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:layout_weight="1"
                    android:gravity="center_horizontal"
                    android:onClick="@handlers::onFollowersClicked"
                    android:orientation="vertical">

                    <TextView
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:fontFamily="sans-serif-condensed"
                        android:text="@BindingUtils.convertToSuffix(user.numberOfFollowers)"
                        android:textColor="@color/profile_meta"
                        android:textSize="24dp" />

                    <TextView
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="@string/followers"
                        android:textSize="@dimen/profile_meta_label" />

                </LinearLayout>

                <LinearLayout
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:layout_weight="1"
                    android:gravity="center_horizontal"
                    android:onClick="@handlers::onFollowingClicked"
                    android:orientation="vertical">

                    <TextView
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:fontFamily="sans-serif-condensed"
                        android:text="@BindingUtils.convertToSuffix(user.numberOfFollowing)"
                        android:textColor="@color/profile_meta"
                        android:textSize="@dimen/profile_meta" />

                    <TextView
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="@string/following"
                        android:textSize="@dimen/profile_meta_label" />

                </LinearLayout>
            </LinearLayout>

            <android.support.v7.widget.RecyclerView
                android:id="@+id/recycler_view"
                android:layout_width="match_parent"
                android:layout_height="match_parent" />
        </LinearLayout>

    </android.support.v4.widget.NestedScrollView>
</layout>

13. Finalmente abierto MainActivity.java y realice los siguientes cambios.

  • ¿Cuál es el nombre del diseño de la actividad principal? activity_mainserá la clase de enlace generada ActivityMainBinding.
  • renderProfile () muestra la información del usuario como nombre, descripción, publicaciones, seguidores y el siguiente número.
  • initRecyclerView () Inicializa RecyclerView con datos de imagen de muestra.
  • MyClickHandlers maneja los eventos de clic de los elementos de la interfaz de usuario. Aquí, toda la vinculación de eventos de clic se lleva a cabo solo a través del diseño XML. No asignamos nada explícitamente al código de actividad.
package info.androidhive.databinding.view;

import android.content.Context;
import android.content.res.Resources;
import android.databinding.DataBindingUtil;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
import android.util.TypedValue;
import android.view.View;
import android.widget.Toast;

import java.util.ArrayList;

import info.androidhive.databinding.R;
import info.androidhive.databinding.databinding.ActivityMainBinding;
import info.androidhive.databinding.model.Post;
import info.androidhive.databinding.model.User;
import info.androidhive.databinding.utils.GridSpacingItemDecoration;

public class MainActivity extends AppCompatActivity implements PostsAdapter.PostsAdapterListener 

    private MyClickHandlers handlers;
    private PostsAdapter mAdapter;
    private RecyclerView recyclerView;
    private ActivityMainBinding binding;
    private User user;

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);

        binding = DataBindingUtil.setContentView(this, R.layout.activity_main);

        Toolbar toolbar = binding.toolbar;
        setSupportActionBar(toolbar);
        getSupportActionBar().setTitle(R.string.toolbar_profile);
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);

        handlers = new MyClickHandlers(this);

        renderProfile();

        initRecyclerView();
    

    /**
     * Renders RecyclerView with Grid Images in 3 columns
     */
    private void initRecyclerView() 
        recyclerView = binding.content.recyclerView;
        recyclerView.setLayoutManager(new GridLayoutManager(this, 3));
        recyclerView.addItemDecoration(new GridSpacingItemDecoration(3, dpToPx(4), true));
        recyclerView.setItemAnimator(new DefaultItemAnimator());
        recyclerView.setNestedScrollingEnabled(false);
        mAdapter = new PostsAdapter(getPosts(), this);
        recyclerView.setAdapter(mAdapter);
    

    /**
     * Renders user profile data
     */
    private void renderProfile() 
        user = new User();
        user.setName("David Attenborough");
        user.setEmail("david@natgeo.com");
        user.setProfileImage("https://api.androidhive.info/images/nature/david.jpg");
        user.setAbout("Naturalist");

        // ObservableField doesn't have setter method, instead will
        // be called using set() method
        user.numberOfPosts.set(3400L);
        user.numberOfFollowers.set(3050890L);
        user.numberOfFollowing.set(150L);


        // display user
        binding.setUser(user);

        // assign click handlers
        binding.content.setHandlers(handlers);
    

    private ArrayList<Post> getPosts() 
        ArrayList<Post> posts = new ArrayList<>();
        for (int i = 1; i < 10; i++) 
            Post post = new Post();
            post.setImageUrl("https://api.androidhive.info/images/nature/" + i + ".jpg");

            posts.add(post);
        

        return posts;
    

    @Override
    public void onPostClicked(Post post) 
        Toast.makeText(getApplicationContext(), "Post clicked! " + post.getImageUrl(), Toast.LENGTH_SHORT).show();
    

    public class MyClickHandlers 

        Context context;

        public MyClickHandlers(Context context) 
            this.context = context;
        

        /**
         * Demonstrating updating bind data
         * Profile name, number of posts and profile image
         * will be updated on Fab click
         */
        public void onProfileFabClicked(View view) 
            user.setName("Sir David Attenborough");
            user.setProfileImage("https://api.androidhive.info/images/nature/david1.jpg");

            // updating ObservableField
            user.numberOfPosts.set(5500L);
            user.numberOfFollowers.set(5050890L);
            user.numberOfFollowing.set(180L);
        

        public boolean onProfileImageLongPressed(View view) 
            Toast.makeText(getApplicationContext(), "Profile image long pressed!", Toast.LENGTH_LONG).show();
            return false;
        


        public void onFollowersClicked(View view) 
            Toast.makeText(context, "Followers is clicked!", Toast.LENGTH_SHORT).show();
        

        public void onFollowingClicked(View view) 
            Toast.makeText(context, "Following is clicked!", Toast.LENGTH_SHORT).show();
        

        public void onPostsClicked(View view) 
            Toast.makeText(context, "Posts is clicked!", Toast.LENGTH_SHORT).show();
        
    

    /**
     * Converting dp to pixel
     */
    private int dpToPx(int dp) 
        Resources r = getResources();
        return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, r.getDisplayMetrics()));
    

Ejecute la aplicación una vez y pruébela. Asegúrese de que el dispositivo tenga una conexión a Internet ya que las imágenes se descargan de la red.

Si tiene alguna pregunta, publíquela en la sección siguiente.

LEER  ¿Hackeado? Desconocidos tuvieron acceso a la base de datos.

Publicaciones relacionadas

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Botón volver arriba