Created
November 23, 2025 19:27
-
-
Save sunmeat/f964612dfca8ecb05b417eb29bd116e6 to your computer and use it in GitHub Desktop.
retrofit android example
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| build.gradle.kts (Module): | |
| dependencies { | |
| ... | |
| implementation("androidx.recyclerview:recyclerview:1.3.2") | |
| implementation("com.squareup.retrofit2:retrofit:3.0.0") | |
| implementation("com.squareup.retrofit2:converter-gson:3.0.0") | |
| implementation("com.google.code.gson:gson:2.11.0") | |
| } | |
| ================================================================================================================ | |
| MainActivity.java: | |
| package site.sunmeat.helloworld; | |
| import android.os.*; | |
| import android.view.*; | |
| import android.widget.*; | |
| import android.widget.Toast; | |
| import androidx.annotation.NonNull; | |
| import androidx.appcompat.app.AppCompatActivity; | |
| import androidx.recyclerview.widget.*; | |
| import com.google.gson.annotations.*; | |
| import java.util.*; | |
| import java.util.concurrent.*; | |
| import retrofit2.*; | |
| import retrofit2.converter.gson.GsonConverterFactory; | |
| import retrofit2.http.*; | |
| public class MainActivity extends AppCompatActivity { | |
| private RecyclerView recyclerView; | |
| private ProgressBar progressBar; | |
| private JokeAdapter adapter; | |
| private final ArrayList<JokeModel> jokes = new ArrayList<>(); | |
| private static final int TOTAL_JOKES = 20; | |
| // === Retrofit 3.0.0 інтерфейс === | |
| public interface JokeApi { | |
| @GET("joke/Programming") | |
| Call<JokeModel> getJoke(@Query("type") String type); // параметр не обов’язковий, для сумісності | |
| } | |
| @Override | |
| protected void onCreate(Bundle savedInstanceState) { | |
| super.onCreate(savedInstanceState); | |
| setContentView(R.layout.activity_main); | |
| recyclerView = findViewById(R.id.posts_recycle_view); | |
| progressBar = findViewById(R.id.progressBar); | |
| recyclerView.setLayoutManager(new LinearLayoutManager(this)); | |
| adapter = new JokeAdapter(jokes); | |
| recyclerView.setAdapter(adapter); | |
| // === Retrofit 3.0.0 === | |
| Retrofit retrofit = new Retrofit.Builder() | |
| .baseUrl("https://v2.jokeapi.dev/") | |
| .addConverterFactory(GsonConverterFactory.create()) | |
| .build(); | |
| JokeApi api = retrofit.create(JokeApi.class); | |
| loadJokes(api); | |
| } | |
| private void loadJokes(JokeApi api) { | |
| ExecutorService executor = Executors.newSingleThreadExecutor(); | |
| var mainHandler = new Handler(Looper.getMainLooper()); | |
| executor.execute(() -> { | |
| int loaded = 0; | |
| while (loaded < TOTAL_JOKES) { | |
| try { | |
| Call<JokeModel> call = api.getJoke(""); // або просто null | |
| retrofit2.Response<JokeModel> response = call.execute(); | |
| if (response.isSuccessful() && response.body() != null) { | |
| JokeModel joke = response.body(); | |
| mainHandler.post(() -> { | |
| jokes.add(joke); | |
| adapter.notifyItemInserted(jokes.size() - 1); | |
| recyclerView.scrollToPosition(jokes.size() - 1); | |
| }); | |
| loaded++; | |
| } else { | |
| // якщо помилка – трохи почекаємо і спробуємо ще раз | |
| Thread.sleep(500); | |
| } | |
| } catch (Exception e) { | |
| try { Thread.sleep(1000); } catch (InterruptedException ignored) {} | |
| } | |
| } | |
| mainHandler.post(() -> { | |
| progressBar.setVisibility(View.GONE); | |
| if (jokes.isEmpty()) { | |
| Toast.makeText(this, "Не вдалося завантажити жарти", Toast.LENGTH_LONG).show(); | |
| } | |
| }); | |
| executor.shutdown(); | |
| }); | |
| } | |
| // ==================== адаптер для списку ==================== | |
| private static class JokeAdapter extends RecyclerView.Adapter<JokeAdapter.ViewHolder> { | |
| private final List<JokeModel> jokes; | |
| JokeAdapter(List<JokeModel> jokes) { | |
| this.jokes = jokes; | |
| } | |
| @NonNull | |
| @Override | |
| public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { | |
| View view = LayoutInflater.from(parent.getContext()) | |
| .inflate(R.layout.post_item, parent, false); | |
| return new ViewHolder(view); | |
| } | |
| @Override | |
| public void onBindViewHolder(@NonNull ViewHolder holder, int position) { | |
| String content = jokes.get(position).getContent(); | |
| content = content.replaceAll("! -", "\n-"); | |
| holder.textView.setText(content); | |
| } | |
| @Override | |
| public int getItemCount() { | |
| return jokes.size(); | |
| } | |
| static class ViewHolder extends RecyclerView.ViewHolder { | |
| TextView textView; | |
| ViewHolder(View itemView) { | |
| super(itemView); | |
| textView = itemView.findViewById(R.id.post); | |
| } | |
| } | |
| } | |
| // ==================== модель ==================== | |
| public static class JokeModel { | |
| @SerializedName("type") | |
| @Expose | |
| private String type; | |
| @SerializedName("joke") | |
| @Expose | |
| private String joke; | |
| @SerializedName("setup") | |
| @Expose | |
| private String setup; | |
| @SerializedName("delivery") | |
| @Expose | |
| private String delivery; | |
| public String getContent() { | |
| if ("single".equals(type) && joke != null) { | |
| return joke; | |
| } else if ("twopart".equals(type) && setup != null && delivery != null) { | |
| return setup + "\n\n" + delivery; | |
| } | |
| return "Жарт не розпарсено"; | |
| } | |
| } | |
| } | |
| ================================================================================================================ | |
| activity_main.xml: | |
| <?xml version="1.0" encoding="utf-8"?> | |
| <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" | |
| android:id="@+id/activity_main" | |
| android:layout_width="match_parent" | |
| android:layout_height="match_parent" | |
| android:background="#121926"> | |
| <androidx.recyclerview.widget.RecyclerView | |
| android:id="@+id/posts_recycle_view" | |
| android:layout_width="match_parent" | |
| android:layout_height="match_parent" | |
| android:layout_alignParentStart="true" | |
| android:layout_alignParentTop="true" | |
| android:background="@android:color/transparent" | |
| android:clipToPadding="false" | |
| android:padding="4dp" /> | |
| <ProgressBar | |
| android:id="@+id/progressBar" | |
| android:layout_width="wrap_content" | |
| android:layout_height="wrap_content" | |
| android:layout_centerInParent="true" | |
| android:indeterminateTint="#FFFFFF" | |
| android:visibility="visible" /> | |
| </RelativeLayout> | |
| ================================================================================================================ | |
| post_item.xml: | |
| <?xml version="1.0" encoding="utf-8"?> | |
| <androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android" | |
| android:layout_width="match_parent" | |
| android:layout_height="wrap_content" | |
| android:layout_marginStart="8dp" | |
| android:layout_marginTop="4dp" | |
| android:layout_marginEnd="8dp" | |
| android:layout_marginBottom="4dp"> | |
| <LinearLayout | |
| android:layout_width="match_parent" | |
| android:layout_height="wrap_content" | |
| android:background="@android:color/transparent" | |
| android:orientation="vertical" | |
| android:padding="12dp"> | |
| <TextView | |
| android:id="@+id/post" | |
| android:layout_width="match_parent" | |
| android:layout_height="wrap_content" | |
| android:background="#FFFFFF" | |
| android:fontFamily="sans-serif" | |
| android:lineSpacingExtra="2dp" | |
| android:padding="4dp" | |
| android:textColor="#000000" | |
| android:textSize="16sp" | |
| android:textStyle="normal" /> | |
| </LinearLayout> | |
| </androidx.cardview.widget.CardView> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment