Problem z Fragmentem i klawiaturą

0

Witam.

Stosunkowo niedawno rozpocząłem programowanie na Androida. Obecnie próbuję napisać swoją prostą aplikację (aha, piszę jeszcze niestety w Javie jak coś). Moja aplikacja działa w taki sposób, żę mam jakby gówną aktywność, w której dynamicznie podmieniam fragmenty (FragmentManager i te sprawy). Problem pojawił się w momencie, w którym jeden z fragmentów ma EditText'y no i jak wyświetla się klawiatura - to pod warunkiem, że użytkownik nie naciśnie "done" - gdy przejdzie do innego fragmentu (podmienie fragment) klawiatura się nie zamyka, zasłaniająć część innego fragmentu. Moje pytanie brzmi - jak powinno się to zrobić, żeby klawiatura zamykała się przy przejściu do innego fragmentu, a otwierała (pod warunkiem że była otwarta przy zamknięciu) po powrocie do tego fragmentu (a i klawisz "done" znika też przy przejściu do innego fragmentu - ale dodanie go (co bym chyba potrafił) nic nie zmieni)?

Ogólnie myślałem o metodach onStop i onResume ale nie jest to chyba dobre wyjście.

Pomoże ktoś, bo moje doświadczenie jest aktualnie dosyć nikłe?

Poniższy kod jest z pewnością daleki od doskonałości (i optymalności) ale mam nadzieję, że ktoś zdoła mi trochę pomóc. A i KONSTRUKTYWNĄ krytykę odnośnie kodu też lubię. ;)

Z góry dzięki.

Poniżej XML z layoutem tego fragmentu (chyba tutaj nie potrzebny bo trzeba to zrobić kodem?)

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <android.support.design.widget.TextInputLayout
        android:id="@+id/frameLayout"
        android:layout_width="210dp"
        android:layout_height="40dp"
        android:layout_alignLeft="@+id/pic_preview"
        android:layout_alignStart="@+id/pic_preview"
        android:layout_below="@+id/frameLayout2"
        android:layout_marginLeft="49dp"
        android:layout_marginStart="49dp"
        android:layout_marginTop="36dp">

        <EditText
            android:id="@+id/edit_text_name"
            android:layout_width="@dimen/edit_text_width"
            android:layout_height="@dimen/edit_text_height"
            android:layout_marginBottom="35dp"
            android:hint="@string/not_working"
            android:imeOptions="actionDone"
            android:inputType="text"/>
    </android.support.design.widget.TextInputLayout>

    <android.support.design.widget.TextInputLayout
        android:id="@+id/frameLayout2"
        android:layout_width="210dp"
        android:layout_height="40dp"
        android:layout_alignLeft="@+id/frameLayout"
        android:layout_alignParentTop="true"
        android:layout_alignStart="@+id/frameLayout"
        android:layout_marginTop="35dp">

        <EditText
            android:id="@+id/editText"
            android:layout_width="@dimen/edit_text_width"
            android:layout_height="@dimen/edit_text_height"
            android:layout_marginBottom="65dp"
            android:hint="@string/hint_name"
            android:paddingTop="5dp"
            android:imeOptions="actionDone"
            android:inputType="text"/>

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

    <ImageView
        android:id="@+id/pic_preview"
        android:layout_width="110dp"
        android:layout_height="110dp"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:layout_centerVertical="true"
        android:src="@drawable/android"
        android:layout_marginLeft="22dp"
        android:layout_marginStart="22dp" />

    <Button
        android:id="@+id/chose_pic"
        android:layout_width="110dp"
        android:layout_height="45dp"
        android:layout_alignLeft="@+id/pic_preview"
        android:layout_alignStart="@+id/pic_preview"
        android:layout_below="@+id/pic_preview"
        android:layout_toLeftOf="@id/chose_pic"
        android:layout_marginTop="13dp" />



</RelativeLayout>

public class PicsChooserFrag extends Fragment {

    private boolean keyboard_on;

   
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup viewGroup, Bundle bundle){
        RelativeLayout relative=(RelativeLayout)inflater.inflate(R.layout.image_chooser,viewGroup,false);
        return relative;
    }
//????????????????????????????????????????
    public PicsChooserFrag(){
        super();
        keyboard_on=false;
    }

    //onStop - when user doesn't see the fragment any more
    //onREsume - when user see the fragment  once more

    @Override
    public void onStop(){
        super.onStop();

    }

    @Override
    public void onResume(){
        super.onResume();
    }




}
public class MainActivity extends AppCompatActivity {

    // ListView representing options in the drawer
    private ListView listViewOfDrawer;
    // reference to whole DrawerLayout containing ToolBar, mainFragment and ListView with drawer options
    private DrawerLayout wholeDrawer;
    private String[]titles; // array representing string shown in drawer
    //ActionBarDrawerToggler provides a handy way to tie together the funcionallyty of DrawerLAyout and the framework ActionBar
    //  "makes the hamburger work"
    private ActionBarDrawerToggle drawerToggle;

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

        titles=getResources().getStringArray(R.array.title);

        //operations concerning ListView with options of drawer
        listViewOfDrawer=(ListView)findViewById(R.id.drawer_list_view);
        //setting sample content of Drawer using ArrayAdapter
        listViewOfDrawer.setAdapter(new ArrayAdapter<String>(this,android.R.layout.simple_list_item_activated_1,titles));
        //adding listview listener to the ListView representing "drawer" options
        listViewOfDrawer.setOnItemClickListener(new DrawerClickListener());

      
        wholeDrawer=(DrawerLayout)findViewById(R.id.drawerLayout);


  
        getFragmentManager().addOnBackStackChangedListener(
                new FragmentManager.OnBackStackChangedListener(){
                    public void onBackStackChanged(){
                      FragmentManager fragmentManager=getFragmentManager();
                      Fragment fragment=fragmentManager.findFragmentByTag("fragment");

                        if(fragment instanceof PicsFragment ){
                            // actions when PicsFragment was added
                            Toast.makeText(getBaseContext(),"PicsFragment",Toast.LENGTH_SHORT).show();//getBaseContext ???

                        }else if(fragment instanceof PicsChooserFrag){
                            Toast.makeText(getBaseContext(),"PicsChooserFrag",Toast.LENGTH_SHORT).show();//getBaseContext ???

                        }

                    }
                }
        );




       
       Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);

        setSupportActionBar(toolbar);
        //displaying hamburger button
        getSupportActionBar().setHomeButtonEnabled(true);
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);




        // making the hamburger work
        drawerToggle=new ActionBarDrawerToggle(this,wholeDrawer,R.string.app_name,R.string.app_name){

            @Override
            public void onDrawerClosed(View drawerView){
              //  Toast.makeText(getApplicationContext(),"Zamknieto Menu",Toast.LENGTH_SHORT).show();
                super.onDrawerClosed(drawerView);
                invalidateOptionsMenu();
            }

            @Override
            public void onDrawerOpened(View drawerView){
              //  Toast.makeText(getApplicationContext(),"Otworzono Menu",Toast.LENGTH_SHORT).show();
                invalidateOptionsMenu();//declare that options menu has changed , so should be redeclared
            }

        };


        wholeDrawer.addDrawerListener(drawerToggle);
        drawerToggle.syncState();




    }

    // implementation of "OnItemClickListViewListener" interface
    private class DrawerClickListener implements ListView.OnItemClickListener{

        @Override
        public void onItemClick(AdapterView<?> praent,View view, int position,long id){

            selectItem(position);
        }
    }

    private void selectItem(int position){
        Fragment fragmentToPut=null;
        switch(position){
            case 0:
                fragmentToPut=new PicsFragment();
                break;
            case 1:
                fragmentToPut=new PicsChooserFrag();
                Toast.makeText(this,"Choosing...", Toast.LENGTH_LONG).show();
                break;
            default:
               
                fragmentToPut=new PicsFragment();// default
                Toast.makeText(this,"Option doesn't work yet :(",Toast.LENGTH_SHORT).show();
        }
        //we replace the fragment ...
        FragmentTransaction fragTran=getFragmentManager().beginTransaction();
        fragTran.replace(R.id.main_fragment,fragmentToPut,"fragment");
        fragTran.addToBackStack(null);
        //selects standard transaction animation for this transaction
        fragTran.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
        fragTran.commit();
        //we close the drawer
        wholeDrawer.closeDrawer(listViewOfDrawer,false);
    }


    // this method places menu options to menu
    //called only once, the first time the options menu is created
    @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) {
       
        int id = item.getItemId();
   
        if (id == R.id.action_settings) {
            Toast.makeText(this,"Options",Toast.LENGTH_SHORT).show();

        



        }else if(id == R.id.author){
            Toast.makeText(this,"Author",Toast.LENGTH_SHORT).show();
        }else{
            //showing menu
            Toast.makeText(this,"Menu",Toast.LENGTH_SHORT).show();
            wholeDrawer.openDrawer(Gravity.START);
        }

        return super.onOptionsItemSelected(item);
    }


  
    @Override
    protected void onPostCreate(Bundle savedInstanceState) {
        super.onPostCreate(savedInstanceState);
        
        drawerToggle.syncState();
    }


    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        drawerToggle.onConfigurationChanged(newConfig);
    }





}

1

co do chowania klawiatury to zobacz tutaj
https://stackoverflow.com/questions/1109022/close-hide-the-android-soft-keyboard
zrob sobie z tego metode statyczna w jakiejs klasie np. ViewUtils

co do otwierania klawy jak ktos wroci do fragmentu na ktorym juz byl to trudniejsza sprawa, sprawdze jutro Twoj kod bo dzisiaj juz nie mam czasu

0

Dobra, już wczoraj nie zdążyłem odpowiedzieć.

Otóż stowrzyłem sobie tą klasę "narzędziową" (nie wklejam bo to jest to co na Stacku) i w klasie PicsChooserFrag dałem ją w metodzie onPause:

@Override
    public void onPause(){
        super.onPause();
        
        ViewUtils.hideKeyBoard(getActivity());


    }


w sumie prawie działa ale problemem jest teraz to, że jak pojawi się klawiatura i otworzę szufladę (żeby włączyć inny fragment) to ta klawiatura zasłania połowę tej szulfady (gdzie potencjalnie mogą być jakieś opcje)...
Pomyslałem więc o metodzie onDrawerOpened ActionBarDrawerToggle'ra (bo chyba zamykanie ewentualnie zamkniętej klawiatury nie będzie problemem - przynajmniej teoretycznie bo chyba nie jest to dobre dla optymalizacji) tylko jak tam przekazać aktywność? :( a może da się jakoś inaczej? Ponieżej fragment kodu z MainActivity, gdzie chciałbym to wstawić:

  // making the hamburger work
        drawerToggle=new ActionBarDrawerToggle(this,wholeDrawer,R.string.app_name,R.string.app_name){

            @Override
            public void onDrawerClosed(View drawerView){
              //  Toast.makeText(getApplicationContext(),"Zamknieto Menu",Toast.LENGTH_SHORT).show();
                super.onDrawerClosed(drawerView);
                invalidateOptionsMenu();
            }

            @Override
            public void onDrawerOpened(View drawerView){
              //  Toast.makeText(getApplicationContext(),"Otworzono Menu",Toast.LENGTH_SHORT).show();
                invalidateOptionsMenu();//declare that options menu has changed , so should be redeclared


                //TODO tutaj chciałbym wstawić ten kod zamykający klawiaturę



            }

        };

a co do otwierania, pod warunkiem, że była otwarta, to jeszcze nic nie wymyśliłem chyba ale ostatecznie nie jest to takie ważne - chciaż jak ktoś ma jakiś pomysł... ;)

Pozdrawiam i dzięki za wszelkie wskazówki.

1
Chungu napisał(a):

tylko jak tam przekazać aktywność? :
Pozdrawiam i dzięki za wszelkie wskazówki.

A to nie działa?

ViewUtils.hideKeyBoard(MainActivity.this);
0

Ok, dzięki działa. ;) Zapomniałem o tym "kwalifikowanym" this :) A wie ktoś może czy updatowanie bazy danych z Fragmentu to dobry pomysł, czy lepiej uruchomić wątek (chyba ASyncTask coś takiego) w MainActivity (a i próbuję użyć ContentProvidera - jeszcze nie wiem jak to połączę ale mam neta i książki ;)) ) - na razie chodzi mi tylko o to czy update z Fragmentu ma jakikolwiek sens i jest "dobrą" praktyką?

Ogólnie mam trochę problem z wykorzystaniem teoretycznej wiedzy w praktyce więc wszelkie wskazówki będę doceniał. :)
Pozdrawiam.

0

To już lepiej załóż nowy wątek z nowym pytaniem, skoro ten oznaczyłeś jako rozwiązany, nie pytaj dalej o coś całkiem innego

1

ContentProvider to zlo... ktos kto to wymyslil powinien za to beknac.

uzyj normalnie SqlLite'a. a co do watkow, idealnie jezeli MainThread (UIThread) powinienes uzywac tylko do aktualizowania widoku (aktywnosc, fragment, view etc)
co sie da powinienes robic w osobnycn watkach.

dobrze sie zarzadza watkami przez RxJava + RxAndroid + Schedulery,
ale naucz sie podstaw Androida na Rx jeszcze przyjdzie czas

0

Ten kto to wymyślił, wie do czego to służy. Ty jak widać nie wiesz.

0

UPDATE: dobra coś znalazłem ale jak ktośchciałby pomóc, to zapraszam. :)

Hm, z fragmentów które czytałem (no i z samej nazwy) wywnioskowałem, żę jest to jakby pośrednik między danymi (np. z BD) a aktywnością no i że można do tego ContentProvidera sięgać też z innych apek (a tym samym do samej BD) jakby w sposób ustandaryzowany, no to chyba lepiej żebym tym robił? :/ Problem jest tego typu, że już (przy drobnej pomocy książki) to zaimplementowałem no i teraz mam problem jak się tym posługiwać (czy wystarczy stworzyć tylko obiekt tego providera i normalnie wywoływać jego metody typu update z odpowiednim Uri???)

Poniżej wstawiam sam kod mojego ContentProvidera (i fragment kodu z DatabaseHlepra tworzącego baze; DataBaseDescription zawiera opis bazy np. nazwy kolumn...)? mógłby ktoś pomóc mi (nie wiem może być jakaś lista kroków/linki do artykułów coś takiego... jak się dostać i zapisać np. obrazek w tej bazie?)

public class GalleryDataBaseContentProvider extends ContentProvider {

    // in order to get access to the db
    private GalleryDatabaseHelper dbHelper;
    private String errorMessage;

    // UriMatcher - usefulclass when writting ContentProvider - it helps to
    //recognize requests
    private static final UriMatcher uriMatcher=new UriMatcher(UriMatcher.NO_MATCH);

    // consts used to recognize the operation
    private static final int ONE_PIC=1;//operation concerning only 1 pic
    private static final int WHOLE_TABLE=2;//operation on the whole table

    // static block configurating UriMAtcher object
    //static block is being called only one when the class is initialized/loaded
    static{
       

        //generally we build up the tree of queries ??
        // Uri address of only one pic - tablename/number
        uriMatcher.addURI(DatabaseDescription.AUTHORITY,DatabaseDescription.Picture.TABLE_NAME_+"/#",ONE_PIC);
        //Uri address of the whole table -  tablename
        uriMatcher.addURI(DatabaseDescription.AUTHORITY,DatabaseDescription.Picture.TABLE_NAME_,WHOLE_TABLE);
    }


    @Override
    public boolean onCreate() {
        dbHelper=new GalleryDatabaseHelper(getContext());
        errorMessage=getContext().getString(R.string.unsupportedQuery);
        return true;
    }


    // this method is not required in this app - return null
    @Nullable
    @Override
    public String getType(@NonNull Uri uri) {
        return null;
    }



 

    @Override
    public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {

        //SQLiteQueryBuilder helps us to build a query
        SQLiteQueryBuilder queryBuilder=new SQLiteQueryBuilder();
        // this builder is concerning our pics table
        queryBuilder.setTables(DatabaseDescription.Picture.TABLE_NAME_);

        // we try to match the uri
        switch(uriMatcher.match(uri)){
            case ONE_PIC:
                // SQL with WHERE clause (where id=id_from_passed_uri)
                queryBuilder.appendWhere(DatabaseDescription.Picture.TABLE_NAME_+"="+uri.getLastPathSegment());

                break;
            case WHOLE_TABLE: // we chose whole table...

                break;
            default:
                unsupportedOperation();
        }


     
        Cursor cursor=queryBuilder.query(dbHelper.getReadableDatabase(),projection,selection,selectionArgs,null,null,sortOrder);

        
        cursor.setNotificationUri(getContext().getContentResolver(),uri);

        return cursor;
    }


    private void unsupportedOperation(){
        // informing with helpful Toast
        Toast.makeText(getContext(),errorMessage,Toast.LENGTH_SHORT).show();
        // we indicate unsupportedoperation by throwing appropriate exception
        throw new UnsupportedOperationException(errorMessage);

    }


    //save new Pic to database
    @Override
    public Uri insert(@NonNull Uri uri, @Nullable ContentValues contentValues) {

        Uri newPicUri=null;

        switch(uriMatcher.match(uri)){
            case WHOLE_TABLE:

                // insert new pic to database
                long insRowId=dbHelper.getWritableDatabase().insert(DatabaseDescription.Picture.TABLE_NAME_,null,contentValues);

                // create Uri address if success
                //else throw an exception
                if(insRowId>0){ // SQLite rows starts at 1
                    // we create new Pic Uri...
                    newPicUri=DatabaseDescription.Picture.createUri(insRowId);


                     // notify listeners that database was modified
                    getContext().getContentResolver().notifyChange(uri,null);
                }else{
                    throw new SQLException(getContext().getString(R.string.insert_failure));
                }

                break;
            default:
                unsupportedOperation();
        }


        return  newPicUri;


    }

    // TODO implement delete method !!!
    @Override
    public int delete(@NonNull Uri uri, @Nullable String s, @Nullable String[] strings) {
        return 0;
    }


    //update previously saved pic
    @Override
    public int update(@NonNull Uri uri, @Nullable ContentValues contentValues, @Nullable String s, @Nullable String[] strings) {

        int numbersOfRowsUpdated=0; // to check if the update was successful

        switch(uriMatcher.match(uri)){
            case ONE_PIC:
                    numbersOfRowsUpdated=dbHelper.getWritableDatabase().update(DatabaseDescription.Picture.TABLE_NAME_,contentValues,DatabaseDescription.Picture._ID+"="+uri.getLastPathSegment(),strings);
                break;
            default:
                unsupportedOperation();
        }


        // if sth was updated we should notifychange
        if(numbersOfRowsUpdated>0){
            getContext().getContentResolver().notifyChange(uri,null);
        }

        return numbersOfRowsUpdated;
    }
}

"SQL CREATE TABLE":

@Override
    public void onCreate(SQLiteDatabase sqLiteDatabase) {
        //SQL creating appropriate table
        final String CREATE_TABLE_SQL_="CREATE_TABLE "+ DatabaseDescription.Picture.TABLE_NAME_+" ("+
                DatabaseDescription.Picture._ID+" integer primary key, "+
                DatabaseDescription.Picture.COLUMN_PIC_NAME+" TEXT, "+
                DatabaseDescription.Picture.COLUMN_PIC_+ " BLOB);";

        sqLiteDatabase.execSQL(CREATE_TABLE_SQL_);


    }

Dzięki ;)

1

W skrócie: jeśli potrzebujesz żeby inne apki mogły sięgać do tych danych, które masz w swojej apce, to potrzebujesz ContentProvidera (zresztą sama jego nazwa chyba wskazuje na przeznaczenie, tak?). A jesli nie potrzebujesz, to sqlite. A jeżeli masz naprawdę mało danych do zapisania i ich struktura jest prosta, to nie potrzebujesz nawet sqlite, wystarczy ci SharedPreferences, który serializuje dane do xml i trzyma w plikach xml.

1

P.S. nie wiem co chcesz konkretnie zrobić, ale jeśli zamierzasz wpychać zdjęcia jako bloby do bazy, to wiedz że jest to złe.

1

Najpierw powiedz, o co ci konkretnie chodzi. Jeśli są to obrazy statyczne, tzn użytkownik nie będzie wgrywał np nowych obrazów, które mają potem być dostępne, oraz są to małe grafiki to drawable. Jeśli mają to być duże obrazy statyczne, to wrzuć raczej do assets jako pliki i wczytuj w programie też jako pliki (przez URI). A jeśli obrazy dynamiczne, to powinny leżeć w systemie plików: w cache lub przestrzeni prywatnej aplikacji (wtedy będą dostępne tylko dla twojej aplikacji) - ale cache użytkownik może wyczyścić, albo gdzieś na karcie SD (np jeden ze standardowych folderów DCIM, PICTURES etc),a w bazie tylko odwołania do nich.

Wpychanie takich wielkich danych bezpośrednio jako bloby bo bazy to bardzo zły pomysł.

0

Dobra, już opisuję:

Jest to apka, która ma przechowywać "śmieszne" obrazki dla użytkownika, użytkownik może "dokładać"/usuwać (DYNAMICZNIE) obrazki do niej oraz może je przeglądać (RecyclerView)- wnioskuję z Twojej wypowiedzi, że powinienem je przechowywać na SD najlepiej - tylko czy mogę razem z danym zdjęciem przechować np. informację w formie Stringa/Tesktu z jakimś krótkim opisem (ale na razie żeby nie udziwniać wystarczy mi odpowiedź czy SD to w takim wypadku dobry pomysł - jak zaimplementuje to, to potem będę udziwniał)? :/

Pozdrawiam.

1

Odpowiedz sobie na pytanie, czy chcesz zabronić użytkownikowi dostępu do zdjęć na karcie, przez menedżera plików np. Jeśli nie jest to konieczne, to stwórz folder np w PICTURES (tylko nie szukaj tego katalogu na sztywno, jest api do pobierania katalogu PICTURES) i tam przechowuj obrazki. Nic nie powinieneś przechowywać razem z obrazkiem, powinno ci wystarczyć odwoływanie się do obrazków po nazwie pliku.

Lub jeśli obrazki mają być widoczne tylko w twojej aplikacji, to możesz przechowywać w folderze prywatnym twojej aplikacji, reszta się nie zmienia poza tym, że nikt nie wejdzie "z boku" i tych plików np nie usunie albo nie pozmienia im nazw - z druiej jednak strony, wtedy zapychasz miejsce na partycji /data (tej, na której instalowane są wszystkie aplikacje) i rozmiar zainstalowanej aplikacji zaczyna puchnąć, użytkownik widzi tylko tyle, że "dane aplikacji" to coraz więcej i więcej, a na niektórych telefonach na partycji /data jest mało miejsca.

Także, wszystko zależy od tego co chcesz osiągnąć i jak ma to działać

0

Tamto pytanie nie miało sensu, jednak teraz mam zagwozdke... jak najlepiej przechowywać w bazie danych referencje do danego obrazka? Jako ścieżka do niego i przy czytaniu z bazy od razu odczytywać dany obraz?

0

Jeśli już, to nie ścieżka tylko uri

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