Android - aplikacja - wyszukiwarka filmów

0

Witam Serdecznie. Chcę zrobić aplikację na Androida którą będzie wyszukiwarka filmów na portalu IMDb (tak wiem istnieje już taka aplikacja ale ja muszą zrobić swoją wersję - jako projekt na studia).

Może zacznę tak... Programowałem jakiś czas temu w C/C++ i aktualnie jestem na etapie nauki, poznawania języka Java.

Jako że nigdy nie robiłem aplikacji chciałbym się dowiedzieć co muszę zrobić żeby to zadziałało.

Otóż idea jest taka:

  • Mamy wyszukiwarkę, po wpisaniu w niej żądanego tytułu wyświetli nam stronę w której znajdować się będą:
    • Tytuł, data premiery filmu, okładka, możliwość odtworzenia trailera, obsada itd.

Wszystkie informacje ma pobierać z serwera IMDb. Przyznam szczerze że nie wiem czy jest taka możliwość, ale jeżeli tak to czego musiałbym się jeszcze podszkolić ?

Chciałbym uzyskać odpowiedź na to, jak ten szkielet musiałby wyglądać żeby w przyszłości ta wyszukiwarka powstała ? Najbardziej zależy mi na informacji w jaki sposób aplikacja jest w stanie pobierać informacje dotyczące filmu pochodzące z IMDb. Nie mam pojęcia jak to funkcjonuje, chociaż podejrzewam że ma to coś wspólnego z Bazami Danych.

0

Z tego co wiem IMDb udostępnia jakieś RESTowe API, także skupiłbym się na tym(np. to ale szczerze nie mam pojęcia czy i jak to działa bo nie robiłem nic w tym temacie http://imdb.wemakesites.net/)
Do tego jakaś biblioteka do parsowania JSON-a np jackson albo gson.

A zacząłbym od postawienia Hello World na androidzie.....

0

Do zapytań HttpDefaultClient i HttpPost/HttpGet z metodami setHeader(). Parsowanie JSON tak jak kolega wyżej napisał. Dostajesz tablice "data" a z niej wyciągasz title i zabierasz url filmu.

0

Dzięki za wskazówki. Jako że nie miałem z tym nigdy styczności (parsowanie) , skąd pozyskać odpowiednią wiedzę ? Są jakieś strony, artykuły w których jest to opisane ? Albo pod jakim hasłem tego szukać (JSON parse ? Czy jeszcze pod innym hasłem ?) Pozdrawiam

0

Jeżeli jest to faktycznie Rest-owy serwis to zainteresuj się Springiem dla Androida i converterem MappingJackson2HttpMessageConverter.
http://projects.spring.io/spring-android/#quick-start

Korzystałem z tego rozwiązania i polecam. W skrócie tworzysz klasę - model na który mapujesz odpowiedź z serwera.
RestTemplate odpowiedzialny jest za wyslanie żądania, odbiór odpowiedzi i zmapowanie na obiekt za pomocą zarejestrowanych konwerterów.
Później do danych dostajesz się przez obiekt tej klasy-modelu.

1

http://square.github.io/retrofit/

tylko i wyłącznie do zapytań;-)

a ja korzystam z kombinacji retrofit + GSON

0

Odkopuję temat bo dopiero teraz zacząłem robić coś w kierunku mojej aplikacji. Ciężki orzech do zgryzienia z tym JSON, tym bardziej że średnio mi idzie JAVA. Nie korzystałem z Jacksona/Gson bo próbowałem coś posklejać wg tutoriali z neta ale chyba nie za bardzo to kumam.

Założenie jest takie aby pobierało odpowiednie dane korzystając z API http://www.omdbapi.com/ . Głównym problemem zrozumienia tego wszystkiego jest to że w większości tutoriali dane JSON są zapisane w tablicach oznaczonych klamrą [] . W moim przypadku dane występują w postaci obiektów - { dane }.

Korzystałem z tego tutoriala: http://www.androidhive.info/2012/01/android-json-parsing-tutorial/ . Wydawał mi się on dość zrozumiały ale gdy zacząłem go przerabiać na potrzeby własnego projektu to w jednym miejscu pojawiło mi się kilka błędów.

Czy jest ktoś w stanie mi pomóc tak aby wg tego tutoriala zamiast pól co tam są - czyli tablice [ ] było np tak:

{
"Title":"American Beauty",
"Year":"1999",
"Rated":"R",
"Released":"01 Oct 1999",
"Runtime":"122 min",
"Genre":"Drama",
"Director":"Sam Mendes",
"Writer":"Alan Ball",
"Actors":"Kevin Spacey, Annette Bening, Thora Birch, Wes Bentley",
"Plot":"Lester and Carolyn Burnham are on the outside, a perfect husband and wife, in a perfect house, in a perfect neighborhood. But inside, Lester is slipping deeper and deeper into a hopeless depression. He finally snaps when he becomes infatuated with one of his daughter's friends. Meanwhile, his daughter Jane is developing a happy friendship with a shy boy-next-door named Ricky, who lives with a homophobic father.",
"Language":"English",
"Country":"USA",
"Awards":"Won 5 Oscars. Another 109 wins & 81 nominations.",
"Poster":"http://ia.media-imdb.com/images/M/MV5BMjM4NTI5NzYyNV5BMl5BanBnXkFtZTgwNTkxNTYxMTE@._V1_SX300.jpg",
"Metascore":"86",
"imdbRating":"8.5",
"imdbVotes":"668,542",
"imdbID":"tt0169547",
"Type":"movie",
"Response":"True"
}

Chciałbym aby po wpisaniu odpowiedniego słowa w wyszukiwarkę dołączało tą frazę do URL - http://www.omdbapi.com/?t=TYTUŁ_FILMU , następnie wyciągnęło np. Title, Year, Released (w zdjęcie się nie bawię póki co bo to pewnie wyższa szkoła jazdy) i wstawiło w jakiś TextView. (Mogę wstawić kod który ja zmodyfikowałem, chodź jestem przekonany że totalne bzdury tam napisałem)

1

tworzysz sobie interfejs, klasę interpreter GSON-a i implementację interjesu, i to wsio


public class Film{
  @SerializedName("Title")
    private String title;

@SerializedName("Released")
  pricate String releasedDate;

//I tak dalej co tam chcesz wyciągnąć. Każde pole tutaj dotyczy pola w JSONie, nie musisz wszystkich wyciągać , tylko to co potrzebne

}

public interface FilmService{
@GET()
public void getFilmInfo(@Query("t") String filmTitle,Callback<FilmInfo> callback);

}

public class FilmApiRequeter {
private final endpoint="http://www.omdbapi.com/";

Rest Adapter restAdapter=new RestAdapter.Builder()
    .setEndpoint(endpoint)
    .build();
   FilmService service=restAdapter.create(FilmService.class);


 public void requestFilm(String title, Callback<Film> callback){
      service.getPlacesForLocation(title, callback);
 }



}


i sobie wywołujesz tylko instancje Requestera i dajesz takiego callbacka jaki Ci się podoba

Wszystko powinno być.

Przykład pisany z palca, nie wiem czy będzie działał. Pewno jakies byki się znajdą

EDIT
Jak pisałem wcześniej zacznij od napisania hello worlda,
tutaj masz też bardzo prosty przykład

http://inaka.net/blog/2014/10/10/android-retrofit-rest-client/

0

Przesiadłem się na darmowe JSON API od rottentomatoes (inny serwis filmowy). Kożystając z GSON+Retrofit chcę aby w metodzie HTTP @GET() zawierało się: /movies.json?apikey=[your_api_key]&q=Jack&page_limit=1 . Założenie jest takie że po wpisaniu w wyszukiwarkę tytułu zapisało owy tytuł w zmienną filmTitle, a zmienna ta odpowiadałaby za to by dopisała to co jest za q=. Aha w adresie jest jeszcze page_limit= . Tutaj chciałbym żeby była możliwość wyświetlania ilości wyników. Nie wiem jak się za to zabrać... Szczególnie nie wiem jak się interpretuje te znaczki "&" w adresie :/

Pełen adres:
http://api.rottentomatoes.com/api/public/v1.0/movies.json?apikey=[your_api_key]&q=Jack&page_limit=1

1
@GET()
public void getFilmInfo(@Query("apikey") String apikey,@Query("q") String filmTitle,@Query("page_limit") String pageLimit,Callback<FilmInfo> callback);
 
}

Znaczniki & robi za Ciebie retrofit, ot taka magia biblioteki

0

Coś mi to wszystko nie wychodzi a projekt do oddania za dwa dni na uczelni :/ . Proszę o pomoc.

Mam coś takiego:

Api.java

import retrofit.Callback;
import retrofit.http.GET;
import retrofit.http.Query;

public interface Api{

    @GET("/movies.json")
    public void getFilmInfo(@Query("apikey") String apikey, @Query("q") String filmTitle, @Query("page_limit") String pageLimit, Callback<Film> callback);

}

Film.java

import com.google.gson.annotations.SerializedName;

public class Film {

    @SerializedName("movies")
    Movies[] movies;

    public Movies[] getMovies() {
        return movies;
    }

    public void setMovies(Movies[] movies) {
        this.movies = movies;

    }
}

Movies.java

import com.google.gson.annotations.SerializedName;

public class Movies {
    @SerializedName("title")
    private String title;
    @SerializedName("year")
    private int year;
    @SerializedName("runtime")
    private int runtime;
    @SerializedName("synopsis")
    private String synopsis;

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public int getYear() {
        return year;
    }

    public void setYear(int year) {
        this.year = year;
    }

    public int getRuntime() {
        return runtime;
    }

    public void setRuntime(int runtime) {
        this.runtime = runtime;
    }

    public String getSynopsis() {
        return synopsis;
    }

    public void setSynopsis(String synopsis) {
        this.synopsis = synopsis;
    }

}

RestAdapter.java

import com.squareup.okhttp.OkHttpClient;
import retrofit.client.OkClient;


public class RestAdapter {

        private static Api REST_CLIENT;
        private static String ROOT ="http://api.rottentomatoes.com/api/public/v1.0";

        static {
            setupRestClient();
        }

        private RestAdapter() {}

        public static Api get() {
            return REST_CLIENT;
        }

        private static void setupRestClient() {
            retrofit.RestAdapter.Builder builder = new retrofit.RestAdapter.Builder()
                    .setEndpoint(ROOT)
                    .setClient(new OkClient(new OkHttpClient()))
                    .setLogLevel(retrofit.RestAdapter.LogLevel.FULL);

            retrofit.RestAdapter restAdapter = builder.build();
            REST_CLIENT = restAdapter.create(Api.class);
        }

}

Nie mam pojęcia jak to zgrać z MainActivity, z języka Java leżę totalnie...
Poza tym wysypały mi się błędy mimo braku błędów w języku związane z " Execution failed for task 'dexDebug' ".
Co do apikey które jest w adresie URL... nie wiem nawet jak sprawić żeby dodawać tam stałą wartość którą otrzymałem z serwisu RottenTomatoes.

0

Jak javy nie znasz to po co sie bierzesz za androida? Po jakiego grzyba Ci te metody statyczne? Wiesz co robi ta komenda setClient?
Potrafisz zadeklarowac pole w klasie typu String? A potrafisz stworzyc instancje klasy uzywajac slowka new? Oprocz pytania nr 1jak odpowiesz sobie na reszte twoje problemy sie rozwiaze

0

Przyznam szczerze robiłem to metodą prób i błędów i nie wiem co robi komenda setClient. Moją ambicją w tym semestrze na studiach była nauka języka Java... niestety ambicje mnie przerosły ze względu na inne przedmioty które utrudniają życie studentom... Obudziłem się za późno (czyt. koniec semestru) i czytam podstawy Retrofita kopiując coś (bezmyślnie), myśląc że się uda.

Z wyżej napisanych postów wydawało mi się że napisanie kodu służącego do parsowania JSON nie będzie czymś trudnym (chciałem pobrać czysty tekst, bez żadnych fotografii bo to już w ogóle dla mnie wyższa szkoła jazdy) ale już na wstępie nie wiedziałem jak połączyć to z Layout'em.

Suma sumarum, chyba się poddam, bo nawet nie wiem ile pracy przede mną a czasu mało...

1

dodaj do tej swojej klasy nieszczęsnej RestAdaptera funkcję publiczną gdzie podasz stringi, później w tej funcji wywołaj

//to w RestAdapter
privat String apikey="tutaj wpisz swoj api key z rottenTomatos";
public void getFilms(String filmTitle,String pageLimit,Callback callback){
restAdapter.getFilmInfo(apikey,filmTitle,pageLimit,callback);
}

a w Activity zrób coś takiego np w onCreate, albo w akcji pod guzikiem albo cuś

//W jakiejś metodzie która gdzieś jest podpięta
RestAdapter.setupRestAdapter();//korzystajac z Twoich dziwacznych staticowych konstrukcji.
RestAdapter.get().getFilmInfo("Ostatni Mohikanin","1",new Callback<Film>{
@Override
            public void success(Film film, Response response) {
               //tutaj se zrob cos ze swoim jasonem filmowy
            }
            @Override
            public void failure(RetrofitError error) {
                error.printStackTrace();//tutaj jakbys cos pokielbasil wypluje Ci blad
            }
        }
});

Naprawdę zabierz się najpierw za hello worldy zamiast za zabawę z Restowym Api

pzdr

0

Panie Wojtku, zacząłem zmieniać klasę RestAdapter (zamieniłem jej nazwę na FilmApiRequerer bo nie podobało mi się że nazywa się tak samo jak klasa pochodząca z Retrofit).

public class FilmApiRequeter {

    private static Api REST_CLIENT;
    private static String ROOT ="http://api.rottentomatoes.com/api/public/v1.0";

    static {
        setupRestClient();
    }

    private FilmApiRequeter() {}

    public static Api get() {
        return REST_CLIENT;
    }

    private static void setupRestClient() {
        retrofit.RestAdapter.Builder builder = new retrofit.RestAdapter.Builder()
                .setEndpoint(ROOT)
                .setClient(new OkClient(new OkHttpClient()))
                .setLogLevel(retrofit.RestAdapter.LogLevel.FULL);

        retrofit.RestAdapter restAdapter = builder.build();
        REST_CLIENT = restAdapter.create(Api.class);
    }

    private String apikey="98nayjn92fy4jzvcmupuxv2p";
    public void getFilms(String filmTitle,String pageLimit, Callback<Film> callback)
    {
        Api.getFilmInfo(apikey,filmTitle,pageLimit,callback);
    }

}

3 linijkę z Pana postu restAdapter.getFilmInfo(apikey,filmTitle,pageLimit,callback); podmieniłem jak wyżej bo wydaje mi się że to chodzi o nadaniu parametrom w Api.getFilmInfo() tego co nadamy w metodzie getFilms(). Tylko już widzę konflikt że metoda getFilmInfo z Api.class nie jest statyczna i wywala błąd.

Co do Layoutu... W activity_main.xml mam EditText gdzie wpisuję żądany tytuł filmu. Chciałbym żeby właśnie to co tam wpiszę wrzuciło mi do zmiennej filmTitle aby mogło powiązać ją z zapytaniem do serwisu RottenTomatoes. Kiedyś kombinowałem coś w stylu, że to co wpisałem w ten EditText wyświetlało mi w TextView w następnej aktywności (daję przykład) ale nie wiem czy jakoś da się to wykorzystać:

W MainAcitvity:

/** Wywoływana, gdy użytkownik kliknie przycisk Szukaj */
    public void sendMessage(View view) {

        Intent intent = new Intent(this, WynikiWyszukiwania.class);
        EditText editText = (EditText) findViewById(R.id.editText);
        String message = editText.getText().toString();
        intent.putExtra(EXTRA_MESSAGE, message);
        startActivity(intent);

W WynikiWyszukiwania:

    final Activity activity = this;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Pobranie wiadomości od intencji
        Intent intent = getIntent();
        String message = intent.getStringExtra(MainActivity.EXTRA_MESSAGE);

        // Utworzenie widoku tekstu
        TextView textView = new TextView(this);
        textView.setTextSize(40);
        textView.setText(message);

        // Ustawienie widoku tekstu jako układu aktywności
        setContentView(textView);

    }
1

za Pana Wojtka to nie powinienem Ci pomagać :P

Po pierwsze ułatwmy sobie trochę życie.
Słusznie zmieniłeś nazwę co by się nie kićkało nic

Powywalajmy te zmienne statyczne które są nie wiadomo na co, Może idze jakiś za tym głębszy sens ale ja go nie widzę

public class FilmApiRequeter {

    private static String ROOT ="http://api.rottentomatoes.com/api/public/v1.0";
      RestAdapter restAdapter = new RestAdapter.Builder()
                .setEndpoint(ROOT)
                .build();
FilmApiRequeter filmRequester=restAdapter.create(Api.class);
 
    private String apikey="98nayjn92fy4jzvcmupuxv2p"

    public void getFilms(String filmTitle,String pageLimit, Callback<Film> callback)
    {
        Api.getFilmInfo(apikey,filmTitle,pageLimit,callback);
    }
 
}

trochę lepiej nie? przynajmniej mi się tak wydaję że bardziej przejrzysto i zrozumiale.

Dalej robimy tak, w Activiti dodajesz sobie EditText i Buttona po czym na buttonie robisz całą magie

//uwaga pseudokod

public void onClickButtonMethod(View v){
String filmTilte=editText.getText().getString();//tutaj pobierasz sobie text z edit texta, zmienną deklarujesz w onCreate np. editText=(EditText)findViewById(R,id,editTitleText);

FilmApiRequeter filmApi=new FilmApiRequeter();

filmApi.getFilms(filmTitle,"1",new Callback<Film>{
@Override
            public void success(Film film, Response response) {
              onFilmGet(film);
            }
            @Override
            public void failure(RetrofitError error) {
                error.printStackTrace();//tutaj jakbys cos pokielbasil wypluje Ci blad
            }
        }
}
}

dobra to jeszcze trzeba stworzyc jakąś tą magiczną metodę co nam cos zrobi z tymi danymi , np pouzupełnia inny edit text albo coś

public void onFilmGet(Film film){

innEditText.setText(film.getMovies().get(0).getTitle());

}

i ta dam :) chyba bardzie łopatologiczniej się nie da

0

Aplikacja napisana, kompiluje się poprawnie. Nie zawiera błędów składniowych, chociaż są elementy warte uwagi typu:

  • W pliku FilmApiRequester.java w linijce
Api filmRequester = restAdapter.create(Api.class);

filmRequester nie jest nigdzie wykorzystywany.

Niestety po wpisaniu w EditText żądanego tytułu filmu otrzymuję komunikat "Aplikacja Rotten Tomatoes została zatrzymana". Wydaje mi się że jest to związane z wielowątkowością, albo jej brakiem. Czy ktoś widzi problem w tej aplikacji ?

MainActivity.java

import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;

import retrofit.Callback;
import retrofit.RetrofitError;
import retrofit.client.Response;


public class MainActivity extends ActionBarActivity {


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

    }

    public void onClickButtonMethod(View v){
        EditText editText = (EditText) findViewById(R.id.editText);
        String filmTilte = editText.getText().toString();

        FilmApiRequeter filmApi = new FilmApiRequeter();

        filmApi.getFilms(filmTilte,"1",new Callback<Film>() {

           @Override
            public void success(Film film, Response response) {
                onFilmGet(film);
            }

            @Override
            public void failure(RetrofitError error) {
                error.printStackTrace();
            }
        });
    }

    public void onFilmGet(Film film) {
        TextView textView = new TextView(this);
        textView.setText(film.getMovies()[0].getTitle());

    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}

FilmApiRequester.java

import retrofit.Callback;
import retrofit.RestAdapter;
import retrofit.http.Query;


public class FilmApiRequeter {




    private static String ROOT ="http://api.rottentomatoes.com/api/public/v1.0";

    RestAdapter restAdapter = new RestAdapter.Builder()
            .setEndpoint(ROOT)
            .build();
    Api filmRequester = restAdapter.create(Api.class);

    private String apikey="xxxxxxxxxxxxxxxxxxxx";



    public void getFilms(String filmTitle,String pageLimit, Callback<Film> callback)
    {
        Api api = new Api() {
            @Override
            public void getFilmInfo(@Query("apikey") String apikey, @Query("q") String filmTitle, @Query("page_limit") String pageLimit, Callback<Film> callback) {

            }
        };
        api.getFilmInfo(apikey,filmTitle,pageLimit,callback);
    }

}

Api.java

import retrofit.Callback;
import retrofit.http.GET;
import retrofit.http.Query;


public interface Api{

    @GET("/movies.json")
    public void getFilmInfo(@Query("apikey") String apikey, @Query("q") String filmTitle, @Query("page_limit") String pageLimit, Callback<Film> callback);

}

Film.java

import com.google.gson.annotations.SerializedName;

public class Film {

    @SerializedName("movies")
    Movies[] movies;

    public Movies[] getMovies() {
        return movies;
    }

    public void setMovies(Movies[] movies) {
        this.movies = movies;

    }
}

Movies.java

import com.google.gson.annotations.SerializedName;

public class Movies {
    @SerializedName("title")
    private String title;
    @SerializedName("year")
    private int year;
    @SerializedName("runtime")
    private int runtime;
    @SerializedName("synopsis")
    private String synopsis;

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public int getYear() {
        return year;
    }

    public void setYear(int year) {
        this.year = year;
    }

    public int getRuntime() {
        return runtime;
    }

    public void setRuntime(int runtime) {
        this.runtime = runtime;
    }

    public String getSynopsis() {
        return synopsis;
    }

    public void setSynopsis(String synopsis) {
        this.synopsis = synopsis;
    }

}

Jest to kompletny program który łączę z layoutem.

1

Porównaj sobie post nad swoim do swojego i zwróć uwagę na klase FilmApiRequeter

Api.getFilmInfo(apikey,filmTitle,pageLimit,callback); se tylko tą linijkę zmień na
filmRequester.getFilmInfo(apikey,filmTitle,pageLimit,callback);

Generalnie tragedia, nie wiesz co to interfejs, klasa, metoda, a bierzesz się za robienie obsługi RESTA, skrobnij kilka projektów w Javie SE, napisz jakiś prosty program na androida, typu wyświetlenie galerii zdjęć.

I przede wszystkim naucz się czytać dokumentacje

0

Poprawiłem wyżej wymienioną linijkę. Problem z tym że wywalało nas z aplikacji był związany z

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

Który powinien być w manifeście zadeklarowany przed <application> a u mnie był po...

Mimo wszystko gdy klikniemy w button "Szukaj" który odnosi nas po kliknięciu w niego do metody "onClickButtonMethod" nic się nie dzieje (czytać: nie wyświetla się nam żaden element który chcieliśmy pobrać z URL typu: tytuł filmu. Pomysły ?

Edit. Aplikacja działa. Poprawiłem jedną rzecz i hula. Dziękuję za rozwiązanie problemu :)

1 użytkowników online, w tym zalogowanych: 0, gości: 1